1/*
2 * Copyright (C) 2008, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "Location.h"
31
32#include "DOMWindow.h"
33#include "ExceptionCode.h"
34#include "Frame.h"
35#include "FrameLoader.h"
36#include "KURL.h"
37
38namespace WebCore {
39
40Location::Location(Frame* frame)
41    : m_frame(frame)
42{
43}
44
45void Location::disconnectFrame()
46{
47    m_frame = 0;
48}
49
50inline const KURL& Location::url() const
51{
52    ASSERT(m_frame);
53
54    const KURL& url = m_frame->document()->url();
55    if (!url.isValid())
56        return blankURL(); // Use "about:blank" while the page is still loading (before we have a frame).
57
58    return url;
59}
60
61String Location::href() const
62{
63    if (!m_frame)
64        return String();
65
66    return url().string();
67}
68
69String Location::protocol() const
70{
71    if (!m_frame)
72        return String();
73
74    return url().protocol() + ":";
75}
76
77String Location::host() const
78{
79    if (!m_frame)
80        return String();
81
82    // Note: this is the IE spec. The NS spec swaps the two, it says
83    // "The hostname property is the concatenation of the host and port properties, separated by a colon."
84    const KURL& url = this->url();
85    return url.hasPort() ? url.host() + ":" + String::number(url.port()) : url.host();
86}
87
88String Location::hostname() const
89{
90    if (!m_frame)
91        return String();
92
93    return url().host();
94}
95
96String Location::port() const
97{
98    if (!m_frame)
99        return String();
100
101    const KURL& url = this->url();
102    return url.hasPort() ? String::number(url.port()) : "";
103}
104
105String Location::pathname() const
106{
107    if (!m_frame)
108        return String();
109
110    const KURL& url = this->url();
111    return url.path().isEmpty() ? "/" : url.path();
112}
113
114String Location::search() const
115{
116    if (!m_frame)
117        return String();
118
119    const KURL& url = this->url();
120    return url.query().isEmpty() ? "" : "?" + url.query();
121}
122
123String Location::origin() const
124{
125    if (!m_frame)
126        return String();
127    return SecurityOrigin::create(url())->toString();
128}
129
130String Location::hash() const
131{
132    if (!m_frame)
133        return String();
134
135    const String& fragmentIdentifier = url().fragmentIdentifier();
136    return fragmentIdentifier.isEmpty() ? "" : "#" + fragmentIdentifier;
137}
138
139String Location::getParameter(const String& name) const
140{
141    if (!m_frame)
142        return String();
143
144    ParsedURLParameters parameters;
145    url().copyParsedQueryTo(parameters);
146    return parameters.get(name);
147}
148
149void Location::setHref(const String& urlString, DOMWindow* activeWindow, DOMWindow* firstWindow)
150{
151    if (!m_frame)
152        return;
153    m_frame->domWindow()->setLocation(urlString, activeWindow, firstWindow);
154}
155
156void Location::setProtocol(const String& protocol, DOMWindow* activeWindow, DOMWindow* firstWindow, ExceptionCode& ec)
157{
158    if (!m_frame)
159        return;
160    KURL url = m_frame->document()->url();
161    if (!url.setProtocol(protocol)) {
162        ec = SYNTAX_ERR;
163        return;
164    }
165    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
166}
167
168void Location::setHost(const String& host, DOMWindow* activeWindow, DOMWindow* firstWindow)
169{
170    if (!m_frame)
171        return;
172    KURL url = m_frame->document()->url();
173    url.setHostAndPort(host);
174    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
175}
176
177void Location::setHostname(const String& hostname, DOMWindow* activeWindow, DOMWindow* firstWindow)
178{
179    if (!m_frame)
180        return;
181    KURL url = m_frame->document()->url();
182    url.setHost(hostname);
183    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
184}
185
186void Location::setPort(const String& portString, DOMWindow* activeWindow, DOMWindow* firstWindow)
187{
188    if (!m_frame)
189        return;
190    KURL url = m_frame->document()->url();
191    int port = portString.toInt();
192    if (port < 0 || port > 0xFFFF || portString.isEmpty())
193        url.removePort();
194    else
195        url.setPort(port);
196    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
197}
198
199void Location::setPathname(const String& pathname, DOMWindow* activeWindow, DOMWindow* firstWindow)
200{
201    if (!m_frame)
202        return;
203    KURL url = m_frame->document()->url();
204    url.setPath(pathname);
205    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
206}
207
208void Location::setSearch(const String& search, DOMWindow* activeWindow, DOMWindow* firstWindow)
209{
210    if (!m_frame)
211        return;
212    KURL url = m_frame->document()->url();
213    url.setQuery(search);
214    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
215}
216
217void Location::setHash(const String& hash, DOMWindow* activeWindow, DOMWindow* firstWindow)
218{
219    if (!m_frame)
220        return;
221    KURL url = m_frame->document()->url();
222    String oldFragmentIdentifier = url.fragmentIdentifier();
223    String newFragmentIdentifier = hash;
224    if (hash[0] == '#')
225        newFragmentIdentifier = hash.substring(1);
226    url.setFragmentIdentifier(newFragmentIdentifier);
227    // Note that by parsing the URL and *then* comparing fragments, we are
228    // comparing fragments post-canonicalization, and so this handles the
229    // cases where fragment identifiers are ignored or invalid.
230    if (equalIgnoringNullity(oldFragmentIdentifier, url.fragmentIdentifier()))
231        return;
232    m_frame->domWindow()->setLocation(url.string(), activeWindow, firstWindow);
233}
234
235void Location::assign(const String& urlString, DOMWindow* activeWindow, DOMWindow* firstWindow)
236{
237    if (!m_frame)
238        return;
239    m_frame->domWindow()->setLocation(urlString, activeWindow, firstWindow);
240}
241
242void Location::replace(const String& urlString, DOMWindow* activeWindow, DOMWindow* firstWindow)
243{
244    if (!m_frame)
245        return;
246    m_frame->domWindow()->setLocation(urlString, activeWindow, firstWindow, LockHistoryAndBackForwardList);
247}
248
249void Location::reload(DOMWindow* activeWindow)
250{
251    if (!m_frame)
252        return;
253    // FIXME: It's not clear this cross-origin security check is valuable.
254    // We allow one page to change the location of another. Why block attempts to reload?
255    // Other location operations simply block use of JavaScript URLs cross origin.
256    DOMWindow* targetWindow = m_frame->domWindow();
257    if (!activeWindow->securityOrigin()->canAccess(targetWindow->securityOrigin())) {
258        targetWindow->printErrorMessage(targetWindow->crossDomainAccessErrorMessage(activeWindow));
259        return;
260    }
261    if (protocolIsJavaScript(m_frame->document()->url()))
262        return;
263    m_frame->navigationScheduler()->scheduleRefresh();
264}
265
266} // namespace WebCore
267