blob: 24cb53f3652c0ac0e5764ad5c74172b977190dfb [file] [log] [blame]
/*
* Copyright (C) 2008, 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/frame/location.h"
#include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
#include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/frame/dom_window.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
#include "third_party/blink/renderer/core/url/dom_url_utils_read_only.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
Location::Location(DOMWindow* dom_window) : dom_window_(dom_window) {}
void Location::Trace(blink::Visitor* visitor) {
visitor->Trace(dom_window_);
ScriptWrappable::Trace(visitor);
}
inline const KURL& Location::Url() const {
const KURL& url = GetDocument()->Url();
if (!url.IsValid()) {
// Use "about:blank" while the page is still loading (before we have a
// frame).
return BlankURL();
}
return url;
}
void Location::href(USVStringOrTrustedURL& result) const {
result.SetUSVString(Url().StrippedForUseAsHref());
}
String Location::protocol() const {
return DOMURLUtilsReadOnly::protocol(Url());
}
String Location::host() const {
return DOMURLUtilsReadOnly::host(Url());
}
String Location::hostname() const {
return DOMURLUtilsReadOnly::hostname(Url());
}
String Location::port() const {
return DOMURLUtilsReadOnly::port(Url());
}
String Location::pathname() const {
return DOMURLUtilsReadOnly::pathname(Url());
}
String Location::search() const {
return DOMURLUtilsReadOnly::search(Url());
}
String Location::origin() const {
return DOMURLUtilsReadOnly::origin(Url());
}
DOMStringList* Location::ancestorOrigins() const {
DOMStringList* origins = DOMStringList::Create();
if (!IsAttached())
return origins;
for (Frame* frame = dom_window_->GetFrame()->Tree().Parent(); frame;
frame = frame->Tree().Parent()) {
origins->Append(
frame->GetSecurityContext()->GetSecurityOrigin()->ToString());
}
return origins;
}
String Location::toString() const {
USVStringOrTrustedURL result;
href(result);
DCHECK(result.IsUSVString());
return result.GetAsUSVString();
}
String Location::hash() const {
return DOMURLUtilsReadOnly::hash(Url());
}
void Location::setHref(v8::Isolate* isolate,
const USVStringOrTrustedURL& string_or_url,
ExceptionState& exception_state) {
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
LocalDOMWindow* entered_window = EnteredDOMWindow(isolate);
const String& url = GetStringFromTrustedURL(
string_or_url, incumbent_window->document(), exception_state);
if (exception_state.HadException())
return;
SetLocation(url, incumbent_window, entered_window, &exception_state);
}
void Location::setProtocol(v8::Isolate* isolate,
const String& protocol,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
if (!url.SetProtocol(protocol)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"'" + protocol + "' is an invalid protocol.");
return;
}
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setHost(v8::Isolate* isolate,
const String& host,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
url.SetHostAndPort(host);
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setHostname(v8::Isolate* isolate,
const String& hostname,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
url.SetHost(hostname);
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setPort(v8::Isolate* isolate,
const String& port,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
url.SetPort(port);
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setPathname(v8::Isolate* isolate,
const String& pathname,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
url.SetPath(pathname);
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setSearch(v8::Isolate* isolate,
const String& search,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
url.SetQuery(search);
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::setHash(v8::Isolate* isolate,
const String& hash,
ExceptionState& exception_state) {
KURL url = GetDocument()->Url();
String old_fragment_identifier = url.FragmentIdentifier();
String new_fragment_identifier = hash;
if (hash[0] == '#')
new_fragment_identifier = hash.Substring(1);
url.SetFragmentIdentifier(new_fragment_identifier);
// Note that by parsing the URL and *then* comparing fragments, we are
// comparing fragments post-canonicalization, and so this handles the
// cases where fragment identifiers are ignored or invalid.
if (EqualIgnoringNullity(old_fragment_identifier, url.FragmentIdentifier()))
return;
SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
EnteredDOMWindow(isolate), &exception_state);
}
void Location::assign(v8::Isolate* isolate,
const USVStringOrTrustedURL& string_or_url,
ExceptionState& exception_state) {
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
LocalDOMWindow* entered_window = EnteredDOMWindow(isolate);
const String& url = GetStringFromTrustedURL(
string_or_url, incumbent_window->document(), exception_state);
if (exception_state.HadException())
return;
SetLocation(url, incumbent_window, entered_window, &exception_state);
}
void Location::replace(v8::Isolate* isolate,
const USVStringOrTrustedURL& string_or_url,
ExceptionState& exception_state) {
LocalDOMWindow* incumbent_window = IncumbentDOMWindow(isolate);
LocalDOMWindow* entered_window = EnteredDOMWindow(isolate);
const String& url = GetStringFromTrustedURL(
string_or_url, incumbent_window->document(), exception_state);
if (exception_state.HadException())
return;
SetLocation(url, incumbent_window, entered_window, &exception_state,
SetLocationPolicy::kReplaceThisFrame);
}
void Location::reload() {
if (!IsAttached())
return;
if (GetDocument()->Url().ProtocolIsJavaScript())
return;
// reload() is not cross-origin accessible, so |dom_window_| will always be
// local.
ToLocalDOMWindow(dom_window_)
->GetFrame()
->Reload(WebFrameLoadType::kReload,
ClientRedirectPolicy::kClientRedirect);
}
void Location::SetLocation(const String& url,
LocalDOMWindow* current_window,
LocalDOMWindow* entered_window,
ExceptionState* exception_state,
SetLocationPolicy set_location_policy) {
if (!IsAttached())
return;
if (!current_window->GetFrame())
return;
Document* entered_document = entered_window->document();
if (!entered_document)
return;
KURL completed_url = entered_document->CompleteURL(url);
if (completed_url.IsNull())
return;
if (!current_window->GetFrame()->CanNavigate(*dom_window_->GetFrame(),
completed_url)) {
if (exception_state) {
exception_state->ThrowSecurityError(
"The current window does not have permission to navigate the target "
"frame to '" +
url + "'.");
}
return;
}
if (exception_state && !completed_url.IsValid()) {
exception_state->ThrowDOMException(DOMExceptionCode::kSyntaxError,
"'" + url + "' is not a valid URL.");
return;
}
if (dom_window_->IsInsecureScriptAccess(*current_window, completed_url))
return;
// Check the source browsing context's CSP to fulfill the CSP check
// requirement of https://html.spec.whatwg.org/#navigate for javascript URLs.
// Although the spec states we should perform this check on task execution,
// we do this prior to dispatch since the parent frame's CSP may be
// inaccessible if the target frame is out of process.
Document* current_document = current_window->document();
if (current_document && completed_url.ProtocolIsJavaScript() &&
!ContentSecurityPolicy::ShouldBypassMainWorld(current_document)) {
String script_source = DecodeURLEscapeSequences(
completed_url.GetString(), DecodeURLMode::kUTF8OrIsomorphic);
if (!current_document->GetContentSecurityPolicy()->AllowJavaScriptURLs(
nullptr, script_source, current_document->Url(), OrdinalNumber())) {
return;
}
}
V8DOMActivityLogger* activity_logger =
V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld();
if (activity_logger) {
Vector<String> argv;
argv.push_back("LocalDOMWindow");
argv.push_back("url");
argv.push_back(entered_document->Url());
argv.push_back(completed_url);
activity_logger->LogEvent("blinkSetAttribute", argv.size(), argv.data());
}
WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
if (set_location_policy == SetLocationPolicy::kReplaceThisFrame)
frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
dom_window_->GetFrame()->ScheduleNavigation(*current_window->document(),
completed_url, frame_load_type,
UserGestureStatus::kNone);
}
Document* Location::GetDocument() const {
return ToLocalDOMWindow(dom_window_)->document();
}
bool Location::IsAttached() const {
return dom_window_->GetFrame();
}
} // namespace blink