1/* 2 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 3 * Copyright (C) 2004-2007 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22#include "config.h" 23 24#if ENABLE(WML) 25#include "WMLPageState.h" 26 27#include "BackForwardController.h" 28#include "BackForwardList.h" 29#include "Document.h" 30#include "Frame.h" 31#include "HistoryItem.h" 32#include "KURL.h" 33#include "Page.h" 34#include <wtf/text/CString.h> 35 36namespace WebCore { 37 38WMLPageState::WMLPageState(Page* page) 39 : m_page(page) 40 , m_hasAccessControlData(false) 41{ 42} 43 44WMLPageState::~WMLPageState() 45{ 46 m_variables.clear(); 47} 48 49#ifndef NDEBUG 50// Debugging helper for use within gdb 51void WMLPageState::dump() 52{ 53 WMLVariableMap::iterator it = m_variables.begin(); 54 WMLVariableMap::iterator end = m_variables.end(); 55 56 fprintf(stderr, "Dumping WMLPageState (this=%p) associated with Page (page=%p)...\n", this, m_page); 57 for (; it != end; ++it) 58 fprintf(stderr, "\t-> name: '%s'\tvalue: '%s'\n", (*it).first.latin1().data(), (*it).second.latin1().data()); 59} 60#endif 61 62void WMLPageState::reset() 63{ 64 // Remove all the variables 65 m_variables.clear(); 66 67 // Clear the navigation history state 68 if (m_page) 69 m_page->backForward()->client()->clearWMLPageHistory(); 70} 71 72static inline String normalizedHostName(const String& passedHost) 73{ 74 if (passedHost.contains("127.0.0.1")) { 75 String host = passedHost; 76 return host.replace("127.0.0.1", "localhost"); 77 } 78 79 return passedHost; 80} 81 82static inline String hostFromURL(const KURL& url) 83{ 84 // Default to "localhost" 85 String host = normalizedHostName(url.host()); 86 return host.isEmpty() ? "localhost" : host; 87} 88 89static KURL urlForHistoryItem(Frame* frame, HistoryItem* item) 90{ 91 // For LayoutTests we need to find the corresponding WML frame in the test document 92 // to be able to test access-control correctly. Remember that WML is never supposed 93 // to be embedded anywhere, so the purpose is to simulate a standalone WML document. 94 if (frame->document()->isWMLDocument()) 95 return item->url(); 96 97 const HistoryItemVector& childItems = item->children(); 98 HistoryItemVector::const_iterator it = childItems.begin(); 99 const HistoryItemVector::const_iterator end = childItems.end(); 100 101 for (; it != end; ++it) { 102 const RefPtr<HistoryItem> childItem = *it; 103 Frame* childFrame = frame->tree()->child(childItem->target()); 104 if (!childFrame) 105 continue; 106 107 if (Document* childDocument = childFrame->document()) { 108 if (childDocument->isWMLDocument()) 109 return childItem->url(); 110 } 111 } 112 113 return item->url(); 114} 115 116static bool tryAccessHistoryURLs(Page* page, KURL& previousURL, KURL& currentURL) 117{ 118 if (!page) 119 return false; 120 121 Frame* frame = page->mainFrame(); 122 if (!frame || !frame->document()) 123 return false; 124 125 HistoryItem* previousItem = page->backForward()->backItem(); 126 if (!previousItem) 127 return false; 128 129 HistoryItem* currentItem = page->backForward()->currentItem(); 130 if (!currentItem) 131 return false; 132 133 previousURL = urlForHistoryItem(frame, previousItem); 134 currentURL = urlForHistoryItem(frame, currentItem); 135 136 return true; 137} 138 139bool WMLPageState::processAccessControlData(const String& domain, const String& path) 140{ 141 if (m_hasAccessControlData) 142 return false; 143 144 m_hasAccessControlData = true; 145 146 KURL previousURL, currentURL; 147 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 148 return true; 149 150 // Spec: The path attribute defaults to the value "/" 151 m_accessPath = path.isEmpty() ? "/" : path; 152 153 // Spec: The domain attribute defaults to the current decks domain. 154 String previousHost = hostFromURL(previousURL); 155 m_accessDomain = domain.isEmpty() ? previousHost : normalizedHostName(domain); 156 157 // Spec: To simplify the development of applications that may not know the absolute path to the 158 // current deck, the path attribute accepts relative URIs. The user agent converts the relative 159 // path to an absolute path and then performs prefix matching against the PATH attribute. 160 Document* document = m_page->mainFrame() ? m_page->mainFrame()->document() : 0; 161 if (document && previousHost == m_accessDomain && !m_accessPath.startsWith("/")) { 162 String currentPath = currentURL.path(); 163 164 size_t index = currentPath.reverseFind('/'); 165 if (index != WTF::notFound) 166 m_accessPath = document->completeURL(currentPath.left(index + 1) + m_accessPath).path(); 167 } 168 169 return true; 170} 171 172void WMLPageState::resetAccessControlData() 173{ 174 m_hasAccessControlData = false; 175 m_accessDomain = String(); 176 m_accessPath = String(); 177} 178 179bool WMLPageState::canAccessDeck() const 180{ 181 if (!m_hasAccessControlData) 182 return true; 183 184 KURL previousURL, currentURL; 185 if (!tryAccessHistoryURLs(m_page, previousURL, currentURL)) 186 return true; 187 188 if (equalIgnoringFragmentIdentifier(previousURL, currentURL)) 189 return true; 190 191 return hostIsAllowedToAccess(hostFromURL(previousURL)) && pathIsAllowedToAccess(previousURL.path()); 192} 193 194bool WMLPageState::hostIsAllowedToAccess(const String& host) const 195{ 196 // Spec: The access domain is suffix-matched against the domain name portion of the referring URI 197 Vector<String> subdomainsAllowed; 198 if (m_accessDomain.contains('.')) 199 m_accessDomain.split('.', subdomainsAllowed); 200 else 201 subdomainsAllowed.append(m_accessDomain); 202 203 Vector<String> subdomainsCheck; 204 if (host.contains('.')) 205 host.split('.', subdomainsCheck); 206 else 207 subdomainsCheck.append(host); 208 209 Vector<String>::iterator itAllowed = subdomainsAllowed.end() - 1; 210 Vector<String>::iterator beginAllowed = subdomainsAllowed.begin(); 211 212 Vector<String>::iterator itCheck = subdomainsCheck.end() - 1; 213 Vector<String>::iterator beginCheck = subdomainsCheck.begin(); 214 215 bool hostOk = true; 216 for (; itAllowed >= beginAllowed && itCheck >= beginCheck; ) { 217 if (*itAllowed != *itCheck) { 218 hostOk = false; 219 break; 220 } 221 222 --itAllowed; 223 --itCheck; 224 } 225 226 return hostOk; 227} 228 229bool WMLPageState::pathIsAllowedToAccess(const String& path) const 230{ 231 // Spec: The access path is prefix matched against the path portion of the referring URI 232 Vector<String> subpathsAllowed; 233 if (m_accessPath.contains('/')) 234 m_accessPath.split('/', subpathsAllowed); 235 else 236 subpathsAllowed.append(m_accessPath); 237 238 Vector<String> subpathsCheck; 239 if (path.contains('/')) 240 path.split('/', subpathsCheck); 241 else 242 subpathsCheck.append(path); 243 244 Vector<String>::iterator itAllowed = subpathsAllowed.begin(); 245 Vector<String>::iterator endAllowed = subpathsAllowed.end(); 246 247 Vector<String>::iterator itCheck = subpathsCheck.begin(); 248 Vector<String>::iterator endCheck = subpathsCheck.end(); 249 250 bool pathOk = true; 251 for (; itAllowed != endAllowed && itCheck != endCheck; ) { 252 if (*itAllowed != *itCheck) { 253 pathOk = false; 254 break; 255 } 256 257 ++itAllowed; 258 ++itCheck; 259 } 260 261 return pathOk; 262} 263 264} 265 266#endif 267