blob: 2896a0431efaa7c9371a0ec72845409d581fdcee [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
* 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 "core/frame/LocalDOMWindow.h"
#include <memory>
#include <string>
#include <utility>
#include "bindings/core/v8/ScriptController.h"
#include "bindings/core/v8/SourceLocation.h"
#include "bindings/core/v8/WindowProxy.h"
#include "core/css/CSSComputedStyleDeclaration.h"
#include "core/css/CSSRuleList.h"
#include "core/css/DOMWindowCSS.h"
#include "core/css/MediaQueryList.h"
#include "core/css/MediaQueryMatcher.h"
#include "core/css/StyleMedia.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/DOMImplementation.h"
#include "core/dom/FrameRequestCallbackCollection.h"
#include "core/dom/Modulator.h"
#include "core/dom/SandboxFlags.h"
#include "core/dom/ScriptedIdleTaskController.h"
#include "core/dom/SinkDocument.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/dom/UserGestureIndicator.h"
#include "core/dom/events/DOMWindowEventQueue.h"
#include "core/dom/events/ScopedEventQueue.h"
#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
#include "core/events/HashChangeEvent.h"
#include "core/events/MessageEvent.h"
#include "core/events/PageTransitionEvent.h"
#include "core/events/PopStateEvent.h"
#include "core/frame/BarProp.h"
#include "core/frame/DOMVisualViewport.h"
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/External.h"
#include "core/frame/FrameConsole.h"
#include "core/frame/History.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Navigator.h"
#include "core/frame/Screen.h"
#include "core/frame/ScrollToOptions.h"
#include "core/frame/Settings.h"
#include "core/frame/SuspendableTimer.h"
#include "core/frame/VisualViewport.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/html/custom/CustomElementRegistry.h"
#include "core/input/EventHandler.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/appcache/ApplicationCache.h"
#include "core/page/ChromeClient.h"
#include "core/page/CreateWindow.h"
#include "core/page/Page.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/probe/CoreProbes.h"
#include "core/style/ComputedStyle.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "platform/EventDispatchForbiddenScope.h"
#include "platform/Histogram.h"
#include "platform/WebFrameScheduler.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/scroll/ScrollbarTheme.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/weborigin/Suborigin.h"
#include "public/platform/Platform.h"
#include "public/platform/WebScreenInfo.h"
#include "public/platform/site_engagement.mojom-blink.h"
namespace blink {
// Timeout for link preloads to be used after window.onload
static const int kUnusedPreloadTimeoutInSeconds = 3;
class PostMessageTimer final
: public GarbageCollectedFinalized<PostMessageTimer>,
public SuspendableTimer {
USING_GARBAGE_COLLECTED_MIXIN(PostMessageTimer);
public:
PostMessageTimer(LocalDOMWindow& window,
MessageEvent* event,
scoped_refptr<SecurityOrigin> target_origin,
std::unique_ptr<SourceLocation> location,
UserGestureToken* user_gesture_token)
: SuspendableTimer(window.document(), TaskType::kPostedMessage),
event_(event),
window_(&window),
target_origin_(std::move(target_origin)),
location_(std::move(location)),
user_gesture_token_(user_gesture_token),
disposal_allowed_(true) {
probe::AsyncTaskScheduled(window.document(), "postMessage", this);
}
MessageEvent* Event() const { return event_; }
SecurityOrigin* TargetOrigin() const { return target_origin_.get(); }
std::unique_ptr<SourceLocation> TakeLocation() {
return std::move(location_);
}
UserGestureToken* GetUserGestureToken() const {
return user_gesture_token_.get();
}
void ContextDestroyed(ExecutionContext* destroyed_context) override {
SuspendableTimer::ContextDestroyed(destroyed_context);
if (disposal_allowed_)
Dispose();
}
// Eager finalization is needed to promptly stop this timer object.
// (see DOMTimer comment for more.)
EAGERLY_FINALIZE();
virtual void Trace(blink::Visitor* visitor) {
visitor->Trace(event_);
visitor->Trace(window_);
SuspendableTimer::Trace(visitor);
}
// TODO(alexclarke): Override timerTaskRunner() to pass in a document specific
// default task runner.
private:
void Fired() override {
probe::AsyncTask async_task(window_->document(), this);
disposal_allowed_ = false;
window_->PostMessageTimerFired(this);
Dispose();
// Oilpan optimization: unregister as an observer right away.
ClearContext();
}
void Dispose() { window_->RemovePostMessageTimer(this); }
Member<MessageEvent> event_;
Member<LocalDOMWindow> window_;
scoped_refptr<SecurityOrigin> target_origin_;
std::unique_ptr<SourceLocation> location_;
scoped_refptr<UserGestureToken> user_gesture_token_;
bool disposal_allowed_;
};
static void UpdateSuddenTerminationStatus(
LocalDOMWindow* dom_window,
bool added_listener,
WebSuddenTerminationDisablerType disabler_type) {
Platform::Current()->SuddenTerminationChanged(!added_listener);
if (dom_window->GetFrame() && dom_window->GetFrame()->Client())
dom_window->GetFrame()->Client()->SuddenTerminationDisablerChanged(
added_listener, disabler_type);
}
using DOMWindowSet = PersistentHeapHashCountedSet<WeakMember<LocalDOMWindow>>;
static DOMWindowSet& WindowsWithUnloadEventListeners() {
DEFINE_STATIC_LOCAL(DOMWindowSet, windows_with_unload_event_listeners, ());
return windows_with_unload_event_listeners;
}
static DOMWindowSet& WindowsWithBeforeUnloadEventListeners() {
DEFINE_STATIC_LOCAL(DOMWindowSet, windows_with_before_unload_event_listeners,
());
return windows_with_before_unload_event_listeners;
}
static void TrackUnloadEventListener(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithUnloadEventListeners();
if (set.insert(dom_window).is_new_entry) {
// The first unload handler was added.
UpdateSuddenTerminationStatus(dom_window, true, kUnloadHandler);
}
}
static void UntrackUnloadEventListener(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithUnloadEventListeners();
DOMWindowSet::iterator it = set.find(dom_window);
if (it == set.end())
return;
if (set.erase(it)) {
// The last unload handler was removed.
UpdateSuddenTerminationStatus(dom_window, false, kUnloadHandler);
}
}
static void UntrackAllUnloadEventListeners(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithUnloadEventListeners();
DOMWindowSet::iterator it = set.find(dom_window);
if (it == set.end())
return;
set.RemoveAll(it);
UpdateSuddenTerminationStatus(dom_window, false, kUnloadHandler);
}
static void TrackBeforeUnloadEventListener(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithBeforeUnloadEventListeners();
if (set.insert(dom_window).is_new_entry) {
// The first beforeunload handler was added.
UpdateSuddenTerminationStatus(dom_window, true, kBeforeUnloadHandler);
}
}
static void UntrackBeforeUnloadEventListener(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithBeforeUnloadEventListeners();
DOMWindowSet::iterator it = set.find(dom_window);
if (it == set.end())
return;
if (set.erase(it)) {
// The last beforeunload handler was removed.
UpdateSuddenTerminationStatus(dom_window, false, kBeforeUnloadHandler);
}
}
static void UntrackAllBeforeUnloadEventListeners(LocalDOMWindow* dom_window) {
DOMWindowSet& set = WindowsWithBeforeUnloadEventListeners();
DOMWindowSet::iterator it = set.find(dom_window);
if (it == set.end())
return;
set.RemoveAll(it);
UpdateSuddenTerminationStatus(dom_window, false, kBeforeUnloadHandler);
}
LocalDOMWindow::LocalDOMWindow(LocalFrame& frame)
: DOMWindow(frame),
visualViewport_(DOMVisualViewport::Create(this)),
unused_preloads_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &frame),
this,
&LocalDOMWindow::WarnUnusedPreloads),
should_print_when_finished_loading_(false) {}
void LocalDOMWindow::ClearDocument() {
if (!document_)
return;
DCHECK(!document_->IsActive());
// FIXME: This should be part of SuspendableObject shutdown
ClearEventQueue();
unused_preloads_timer_.Stop();
document_->ClearDOMWindow();
document_ = nullptr;
}
void LocalDOMWindow::ClearEventQueue() {
if (!event_queue_)
return;
event_queue_->Close();
event_queue_.Clear();
}
void LocalDOMWindow::AcceptLanguagesChanged() {
if (navigator_)
navigator_->SetLanguagesChanged();
DispatchEvent(Event::Create(EventTypeNames::languagechange));
}
Document* LocalDOMWindow::CreateDocument(const String& mime_type,
const DocumentInit& init,
bool force_xhtml) {
Document* document = nullptr;
if (force_xhtml) {
// This is a hack for XSLTProcessor. See
// XSLTProcessor::createDocumentFromSource().
document = Document::Create(init);
} else {
document = DOMImplementation::createDocument(
mime_type, init,
init.GetFrame() ? init.GetFrame()->InViewSourceMode() : false);
if (document->IsPluginDocument() && document->IsSandboxed(kSandboxPlugins))
document = SinkDocument::Create(init);
}
return document;
}
LocalDOMWindow* LocalDOMWindow::From(const ScriptState* script_state) {
v8::HandleScope scope(script_state->GetIsolate());
return blink::ToLocalDOMWindow(script_state->GetContext());
}
Document* LocalDOMWindow::InstallNewDocument(const String& mime_type,
const DocumentInit& init,
bool force_xhtml) {
DCHECK_EQ(init.GetFrame(), GetFrame());
ClearDocument();
document_ = CreateDocument(mime_type, init, force_xhtml);
event_queue_ = DOMWindowEventQueue::Create(document_.Get());
document_->Initialize();
if (!GetFrame())
return document_;
GetFrame()->GetScriptController().UpdateDocument();
document_->UpdateViewportDescription();
if (GetFrame()->GetPage() && GetFrame()->View()) {
GetFrame()->GetPage()->GetChromeClient().InstallSupplements(*GetFrame());
if (ScrollingCoordinator* scrolling_coordinator =
GetFrame()->GetPage()->GetScrollingCoordinator()) {
scrolling_coordinator->ScrollableAreaScrollbarLayerDidChange(
GetFrame()->View(), kHorizontalScrollbar);
scrolling_coordinator->ScrollableAreaScrollbarLayerDidChange(
GetFrame()->View(), kVerticalScrollbar);
scrolling_coordinator->ScrollableAreaScrollLayerDidChange(
GetFrame()->View());
}
}
GetFrame()->Selection().UpdateSecureKeyboardEntryIfActive();
if (GetFrame()->IsCrossOriginSubframe())
document_->RecordDeferredLoadReason(WouldLoadReason::kCreated);
return document_;
}
EventQueue* LocalDOMWindow::GetEventQueue() const {
return event_queue_.Get();
}
void LocalDOMWindow::EnqueueWindowEvent(Event* event) {
if (!event_queue_)
return;
event->SetTarget(this);
event_queue_->EnqueueEvent(BLINK_FROM_HERE, event);
}
void LocalDOMWindow::EnqueueDocumentEvent(Event* event) {
if (!event_queue_)
return;
event->SetTarget(document_.Get());
event_queue_->EnqueueEvent(BLINK_FROM_HERE, event);
}
void LocalDOMWindow::DispatchWindowLoadEvent() {
#if DCHECK_IS_ON()
DCHECK(!EventDispatchForbiddenScope::IsEventDispatchForbidden());
#endif
// Delay 'load' event if we are in EventQueueScope. This is a short-term
// workaround to avoid Editing code crashes. We should always dispatch
// 'load' event asynchronously. crbug.com/569511.
if (ScopedEventQueue::Instance()->ShouldQueueEvents() && document_) {
TaskRunnerHelper::Get(TaskType::kNetworking, document_)
->PostTask(BLINK_FROM_HERE,
WTF::Bind(&LocalDOMWindow::DispatchLoadEvent,
WrapPersistent(this)));
return;
}
DispatchLoadEvent();
}
void LocalDOMWindow::DocumentWasClosed() {
DispatchWindowLoadEvent();
EnqueuePageshowEvent(kPageshowEventNotPersisted);
if (pending_state_object_)
EnqueuePopstateEvent(std::move(pending_state_object_));
}
void LocalDOMWindow::EnqueuePageshowEvent(PageshowEventPersistence persisted) {
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=36334 Pageshow event needs
// to fire asynchronously. As per spec pageshow must be triggered
// asynchronously. However to be compatible with other browsers blink fires
// pageshow synchronously unless we are in EventQueueScope.
if (ScopedEventQueue::Instance()->ShouldQueueEvents() && document_) {
EnqueueWindowEvent(
PageTransitionEvent::Create(EventTypeNames::pageshow, persisted));
return;
}
DispatchEvent(
PageTransitionEvent::Create(EventTypeNames::pageshow, persisted),
document_.Get());
}
void LocalDOMWindow::EnqueueHashchangeEvent(const String& old_url,
const String& new_url) {
EnqueueWindowEvent(HashChangeEvent::Create(old_url, new_url));
}
void LocalDOMWindow::EnqueuePopstateEvent(
scoped_refptr<SerializedScriptValue> state_object) {
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=36202 Popstate event needs
// to fire asynchronously
DispatchEvent(PopStateEvent::Create(std::move(state_object), history()));
}
void LocalDOMWindow::StatePopped(
scoped_refptr<SerializedScriptValue> state_object) {
if (!GetFrame())
return;
// Per step 11 of section 6.5.9 (history traversal) of the HTML5 spec, we
// defer firing of popstate until we're in the complete state.
if (document()->IsLoadCompleted())
EnqueuePopstateEvent(std::move(state_object));
else
pending_state_object_ = std::move(state_object);
}
LocalDOMWindow::~LocalDOMWindow() {
// Cleared when detaching document.
DCHECK(!event_queue_);
}
void LocalDOMWindow::Dispose() {
// Oilpan: should the LocalDOMWindow be GCed along with its LocalFrame without
// the frame having first notified its observers of imminent destruction, the
// LocalDOMWindow will not have had an opportunity to remove event listeners.
//
// Arrange for that removal to happen using a prefinalizer action. Making
// LocalDOMWindow eager finalizable is problematic as other eagerly finalized
// objects may well want to access their associated LocalDOMWindow from their
// destructors.
if (!GetFrame())
return;
RemoveAllEventListeners();
}
ExecutionContext* LocalDOMWindow::GetExecutionContext() const {
return document_.Get();
}
const LocalDOMWindow* LocalDOMWindow::ToLocalDOMWindow() const {
return this;
}
LocalDOMWindow* LocalDOMWindow::ToLocalDOMWindow() {
return this;
}
MediaQueryList* LocalDOMWindow::matchMedia(const String& media) {
return document() ? document()->GetMediaQueryMatcher().MatchMedia(media)
: nullptr;
}
void LocalDOMWindow::FrameDestroyed() {
RemoveAllEventListeners();
DisconnectFromFrame();
}
void LocalDOMWindow::RegisterEventListenerObserver(
EventListenerObserver* event_listener_observer) {
event_listener_observers_.insert(event_listener_observer);
}
void LocalDOMWindow::Reset() {
DCHECK(document());
DCHECK(document()->IsContextDestroyed());
FrameDestroyed();
screen_ = nullptr;
history_ = nullptr;
locationbar_ = nullptr;
menubar_ = nullptr;
personalbar_ = nullptr;
scrollbars_ = nullptr;
statusbar_ = nullptr;
toolbar_ = nullptr;
navigator_ = nullptr;
media_ = nullptr;
custom_elements_ = nullptr;
application_cache_ = nullptr;
}
void LocalDOMWindow::SendOrientationChangeEvent() {
DCHECK(RuntimeEnabledFeatures::OrientationEventEnabled());
DCHECK(GetFrame()->IsLocalRoot());
// Before dispatching the event, build a list of all frames in the page
// to send the event to, to mitigate side effects from event handlers
// potentially interfering with others.
HeapVector<Member<LocalFrame>> frames;
frames.push_back(GetFrame());
for (size_t i = 0; i < frames.size(); i++) {
for (Frame* child = frames[i]->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (child->IsLocalFrame())
frames.push_back(ToLocalFrame(child));
}
}
for (LocalFrame* frame : frames) {
frame->DomWindow()->DispatchEvent(
Event::Create(EventTypeNames::orientationchange));
}
}
int LocalDOMWindow::orientation() const {
DCHECK(RuntimeEnabledFeatures::OrientationEventEnabled());
if (!GetFrame() || !GetFrame()->GetPage())
return 0;
int orientation = GetFrame()
->GetPage()
->GetChromeClient()
.GetScreenInfo()
.orientation_angle;
// For backward compatibility, we want to return a value in the range of
// [-90; 180] instead of [0; 360[ because window.orientation used to behave
// like that in WebKit (this is a WebKit proprietary API).
if (orientation == 270)
return -90;
return orientation;
}
Screen* LocalDOMWindow::screen() const {
if (!screen_)
screen_ = Screen::Create(GetFrame());
return screen_.Get();
}
History* LocalDOMWindow::history() const {
if (!history_)
history_ = History::Create(GetFrame());
return history_.Get();
}
BarProp* LocalDOMWindow::locationbar() const {
if (!locationbar_)
locationbar_ = BarProp::Create(GetFrame(), BarProp::kLocationbar);
return locationbar_.Get();
}
BarProp* LocalDOMWindow::menubar() const {
if (!menubar_)
menubar_ = BarProp::Create(GetFrame(), BarProp::kMenubar);
return menubar_.Get();
}
BarProp* LocalDOMWindow::personalbar() const {
if (!personalbar_)
personalbar_ = BarProp::Create(GetFrame(), BarProp::kPersonalbar);
return personalbar_.Get();
}
BarProp* LocalDOMWindow::scrollbars() const {
if (!scrollbars_)
scrollbars_ = BarProp::Create(GetFrame(), BarProp::kScrollbars);
return scrollbars_.Get();
}
BarProp* LocalDOMWindow::statusbar() const {
if (!statusbar_)
statusbar_ = BarProp::Create(GetFrame(), BarProp::kStatusbar);
return statusbar_.Get();
}
BarProp* LocalDOMWindow::toolbar() const {
if (!toolbar_)
toolbar_ = BarProp::Create(GetFrame(), BarProp::kToolbar);
return toolbar_.Get();
}
FrameConsole* LocalDOMWindow::GetFrameConsole() const {
if (!IsCurrentlyDisplayedInFrame())
return nullptr;
return &GetFrame()->Console();
}
ApplicationCache* LocalDOMWindow::applicationCache() const {
if (!IsCurrentlyDisplayedInFrame())
return nullptr;
if (!application_cache_)
application_cache_ = ApplicationCache::Create(GetFrame());
return application_cache_.Get();
}
Navigator* LocalDOMWindow::navigator() const {
if (!navigator_)
navigator_ = Navigator::Create(GetFrame());
return navigator_.Get();
}
void LocalDOMWindow::SchedulePostMessage(MessageEvent* event,
scoped_refptr<SecurityOrigin> target,
Document* source) {
// Allowing unbounded amounts of messages to build up for a suspended context
// is problematic; consider imposing a limit or other restriction if this
// surfaces often as a problem (see crbug.com/587012).
std::unique_ptr<SourceLocation> location = SourceLocation::Capture(source);
PostMessageTimer* timer =
new PostMessageTimer(*this, event, std::move(target), std::move(location),
UserGestureIndicator::CurrentToken());
timer->StartOneShot(0, BLINK_FROM_HERE);
timer->SuspendIfNeeded();
post_message_timers_.insert(timer);
}
void LocalDOMWindow::PostMessageTimerFired(PostMessageTimer* timer) {
if (!IsCurrentlyDisplayedInFrame())
return;
MessageEvent* event = timer->Event();
UserGestureToken* token = timer->GetUserGestureToken();
UserGestureIndicator gesture_indicator(token);
if (token && token->HasGestures() && document() && document()->GetFrame())
document()->GetFrame()->NotifyUserActivation();
event->EntangleMessagePorts(document());
DispatchMessageEventWithOriginCheck(timer->TargetOrigin(), event,
timer->TakeLocation());
}
void LocalDOMWindow::RemovePostMessageTimer(PostMessageTimer* timer) {
post_message_timers_.erase(timer);
}
void LocalDOMWindow::DispatchMessageEventWithOriginCheck(
SecurityOrigin* intended_target_origin,
Event* event,
std::unique_ptr<SourceLocation> location) {
if (intended_target_origin) {
// Check target origin now since the target document may have changed since
// the timer was scheduled.
SecurityOrigin* security_origin = document()->GetSecurityOrigin();
bool valid_target =
intended_target_origin->IsSameSchemeHostPortAndSuborigin(
security_origin);
if (security_origin->HasSuborigin() &&
security_origin->GetSuborigin()->PolicyContains(
Suborigin::SuboriginPolicyOptions::kUnsafePostMessageReceive))
valid_target =
intended_target_origin->IsSameSchemeHostPort(security_origin);
if (!valid_target) {
String message = ExceptionMessages::FailedToExecute(
"postMessage", "DOMWindow",
"The target origin provided ('" + intended_target_origin->ToString() +
"') does not match the recipient window's origin ('" +
document()->GetSecurityOrigin()->ToString() + "').");
ConsoleMessage* console_message =
ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
message, std::move(location));
GetFrameConsole()->AddMessage(console_message);
return;
}
}
KURL sender(static_cast<MessageEvent*>(event)->origin());
if (!document()->GetContentSecurityPolicy()->AllowConnectToSource(
sender, RedirectStatus::kNoRedirect,
SecurityViolationReportingPolicy::kSuppressReporting)) {
UseCounter::Count(
GetFrame(), WebFeature::kPostMessageIncomingWouldBeBlockedByConnectSrc);
}
DispatchEvent(event);
}
DOMSelection* LocalDOMWindow::getSelection() {
if (!IsCurrentlyDisplayedInFrame())
return nullptr;
return document()->GetSelection();
}
Element* LocalDOMWindow::frameElement() const {
if (!(GetFrame() && GetFrame()->Owner() && GetFrame()->Owner()->IsLocal()))
return nullptr;
return ToHTMLFrameOwnerElement(GetFrame()->Owner());
}
void LocalDOMWindow::blur() {}
void LocalDOMWindow::print(ScriptState* script_state) {
if (!GetFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
if (script_state &&
v8::MicrotasksScope::IsRunningMicrotasks(script_state->GetIsolate())) {
UseCounter::Count(document(), WebFeature::kDuring_Microtask_Print);
}
if (GetFrame()->IsLoading()) {
should_print_when_finished_loading_ = true;
return;
}
UseCounter::CountCrossOriginIframe(*document(),
WebFeature::kCrossOriginWindowPrint);
should_print_when_finished_loading_ = false;
page->GetChromeClient().Print(GetFrame());
}
void LocalDOMWindow::stop() {
if (!GetFrame())
return;
GetFrame()->Loader().StopAllLoaders();
}
void LocalDOMWindow::alert(ScriptState* script_state, const String& message) {
if (!GetFrame())
return;
if (document()->IsSandboxed(kSandboxModals)) {
UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
GetFrameConsole()->AddMessage(ConsoleMessage::Create(
kSecurityMessageSource, kErrorMessageLevel,
"Ignored call to 'alert()'. The document is sandboxed, and the "
"'allow-modals' keyword is not set."));
return;
}
switch (document()->GetEngagementLevel()) {
case mojom::blink::EngagementLevel::NONE:
UseCounter::Count(document(), WebFeature::kAlertEngagementNone);
break;
case mojom::blink::EngagementLevel::MINIMAL:
UseCounter::Count(document(), WebFeature::kAlertEngagementMinimal);
break;
case mojom::blink::EngagementLevel::LOW:
UseCounter::Count(document(), WebFeature::kAlertEngagementLow);
break;
case mojom::blink::EngagementLevel::MEDIUM:
UseCounter::Count(document(), WebFeature::kAlertEngagementMedium);
break;
case mojom::blink::EngagementLevel::HIGH:
UseCounter::Count(document(), WebFeature::kAlertEngagementHigh);
break;
case mojom::blink::EngagementLevel::MAX:
UseCounter::Count(document(), WebFeature::kAlertEngagementMax);
break;
}
if (v8::MicrotasksScope::IsRunningMicrotasks(script_state->GetIsolate())) {
UseCounter::Count(document(), WebFeature::kDuring_Microtask_Alert);
}
document()->UpdateStyleAndLayoutTree();
Page* page = GetFrame()->GetPage();
if (!page)
return;
UseCounter::CountCrossOriginIframe(*document(),
WebFeature::kCrossOriginWindowAlert);
page->GetChromeClient().OpenJavaScriptAlert(GetFrame(), message);
}
bool LocalDOMWindow::confirm(ScriptState* script_state, const String& message) {
if (!GetFrame())
return false;
if (document()->IsSandboxed(kSandboxModals)) {
UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
GetFrameConsole()->AddMessage(ConsoleMessage::Create(
kSecurityMessageSource, kErrorMessageLevel,
"Ignored call to 'confirm()'. The document is sandboxed, and the "
"'allow-modals' keyword is not set."));
return false;
}
switch (document()->GetEngagementLevel()) {
case mojom::blink::EngagementLevel::NONE:
UseCounter::Count(document(), WebFeature::kConfirmEngagementNone);
break;
case mojom::blink::EngagementLevel::MINIMAL:
UseCounter::Count(document(), WebFeature::kConfirmEngagementMinimal);
break;
case mojom::blink::EngagementLevel::LOW:
UseCounter::Count(document(), WebFeature::kConfirmEngagementLow);
break;
case mojom::blink::EngagementLevel::MEDIUM:
UseCounter::Count(document(), WebFeature::kConfirmEngagementMedium);
break;
case mojom::blink::EngagementLevel::HIGH:
UseCounter::Count(document(), WebFeature::kConfirmEngagementHigh);
break;
case mojom::blink::EngagementLevel::MAX:
UseCounter::Count(document(), WebFeature::kConfirmEngagementMax);
break;
}
if (v8::MicrotasksScope::IsRunningMicrotasks(script_state->GetIsolate())) {
UseCounter::Count(document(), WebFeature::kDuring_Microtask_Confirm);
}
document()->UpdateStyleAndLayoutTree();
Page* page = GetFrame()->GetPage();
if (!page)
return false;
UseCounter::CountCrossOriginIframe(*document(),
WebFeature::kCrossOriginWindowConfirm);
return page->GetChromeClient().OpenJavaScriptConfirm(GetFrame(), message);
}
String LocalDOMWindow::prompt(ScriptState* script_state,
const String& message,
const String& default_value) {
if (!GetFrame())
return String();
if (document()->IsSandboxed(kSandboxModals)) {
UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
GetFrameConsole()->AddMessage(ConsoleMessage::Create(
kSecurityMessageSource, kErrorMessageLevel,
"Ignored call to 'prompt()'. The document is sandboxed, and the "
"'allow-modals' keyword is not set."));
return String();
}
switch (document()->GetEngagementLevel()) {
case mojom::blink::EngagementLevel::NONE:
UseCounter::Count(document(), WebFeature::kPromptEngagementNone);
break;
case mojom::blink::EngagementLevel::MINIMAL:
UseCounter::Count(document(), WebFeature::kPromptEngagementMinimal);
break;
case mojom::blink::EngagementLevel::LOW:
UseCounter::Count(document(), WebFeature::kPromptEngagementLow);
break;
case mojom::blink::EngagementLevel::MEDIUM:
UseCounter::Count(document(), WebFeature::kPromptEngagementMedium);
break;
case mojom::blink::EngagementLevel::HIGH:
UseCounter::Count(document(), WebFeature::kPromptEngagementHigh);
break;
case mojom::blink::EngagementLevel::MAX:
UseCounter::Count(document(), WebFeature::kPromptEngagementMax);
break;
}
if (v8::MicrotasksScope::IsRunningMicrotasks(script_state->GetIsolate())) {
UseCounter::Count(document(), WebFeature::kDuring_Microtask_Prompt);
}
document()->UpdateStyleAndLayoutTree();
Page* page = GetFrame()->GetPage();
if (!page)
return String();
String return_value;
if (page->GetChromeClient().OpenJavaScriptPrompt(GetFrame(), message,
default_value, return_value))
return return_value;
UseCounter::CountCrossOriginIframe(*document(),
WebFeature::kCrossOriginWindowPrompt);
return String();
}
bool LocalDOMWindow::find(const String& string,
bool case_sensitive,
bool backwards,
bool wrap,
bool whole_word,
bool /*searchInFrames*/,
bool /*showDialog*/) const {
if (!IsCurrentlyDisplayedInFrame())
return false;
// Up-to-date, clean tree is required for finding text in page, since it
// relies on TextIterator to look over the text.
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
// FIXME (13016): Support searchInFrames and showDialog
FindOptions options =
(backwards ? kBackwards : 0) | (case_sensitive ? 0 : kCaseInsensitive) |
(wrap ? kWrapAround : 0) | (whole_word ? kWholeWord | kAtWordStarts : 0);
return GetFrame()->GetEditor().FindString(string, options);
}
bool LocalDOMWindow::offscreenBuffering() const {
return true;
}
int LocalDOMWindow::outerHeight() const {
if (!GetFrame())
return 0;
Page* page = GetFrame()->GetPage();
if (!page)
return 0;
ChromeClient& chrome_client = page->GetChromeClient();
if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk())
return lroundf(chrome_client.RootWindowRect().Height() *
chrome_client.GetScreenInfo().device_scale_factor);
return chrome_client.RootWindowRect().Height();
}
int LocalDOMWindow::outerWidth() const {
if (!GetFrame())
return 0;
Page* page = GetFrame()->GetPage();
if (!page)
return 0;
ChromeClient& chrome_client = page->GetChromeClient();
if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk())
return lroundf(chrome_client.RootWindowRect().Width() *
chrome_client.GetScreenInfo().device_scale_factor);
return chrome_client.RootWindowRect().Width();
}
FloatSize LocalDOMWindow::GetViewportSize(
IncludeScrollbarsInRect scrollbar_inclusion) const {
if (!GetFrame())
return FloatSize();
LocalFrameView* view = GetFrame()->View();
if (!view)
return FloatSize();
Page* page = GetFrame()->GetPage();
if (!page)
return FloatSize();
// The main frame's viewport size depends on the page scale. If viewport is
// enabled, the initial page scale depends on the content width and is set
// after a layout, perform one now so queries during page load will use the
// up to date viewport.
bool affectedByScale =
page->GetSettings().GetViewportEnabled() && GetFrame()->IsMainFrame();
bool affectedByScrollbars =
scrollbar_inclusion == kExcludeScrollbars &&
!page->GetScrollbarTheme().UsesOverlayScrollbars();
if (affectedByScale || affectedByScrollbars)
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
// FIXME: This is potentially too much work. We really only need to know the
// dimensions of the parent frame's layoutObject.
if (Frame* parent = GetFrame()->Tree().Parent()) {
if (parent && parent->IsLocalFrame())
ToLocalFrame(parent)
->GetDocument()
->UpdateStyleAndLayoutIgnorePendingStylesheets();
}
return FloatSize(view->VisibleContentRect(scrollbar_inclusion).Size());
}
int LocalDOMWindow::innerHeight() const {
if (!GetFrame())
return 0;
FloatSize viewport_size = GetViewportSize(kIncludeScrollbars);
return AdjustForAbsoluteZoom(ExpandedIntSize(viewport_size).Height(),
GetFrame()->PageZoomFactor());
}
int LocalDOMWindow::innerWidth() const {
if (!GetFrame())
return 0;
FloatSize viewport_size = GetViewportSize(kIncludeScrollbars);
return AdjustForAbsoluteZoom(ExpandedIntSize(viewport_size).Width(),
GetFrame()->PageZoomFactor());
}
int LocalDOMWindow::screenX() const {
if (!GetFrame())
return 0;
Page* page = GetFrame()->GetPage();
if (!page)
return 0;
ChromeClient& chrome_client = page->GetChromeClient();
if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk())
return lroundf(chrome_client.RootWindowRect().X() *
chrome_client.GetScreenInfo().device_scale_factor);
return chrome_client.RootWindowRect().X();
}
int LocalDOMWindow::screenY() const {
if (!GetFrame())
return 0;
Page* page = GetFrame()->GetPage();
if (!page)
return 0;
ChromeClient& chrome_client = page->GetChromeClient();
if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk())
return lroundf(chrome_client.RootWindowRect().Y() *
chrome_client.GetScreenInfo().device_scale_factor);
return chrome_client.RootWindowRect().Y();
}
double LocalDOMWindow::scrollX() const {
if (!GetFrame() || !GetFrame()->GetPage())
return 0;
LocalFrameView* view = GetFrame()->View();
if (!view)
return 0;
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
// TODO(bokan): This is wrong when the document.rootScroller is non-default.
// crbug.com/505516.
double viewport_x =
view->LayoutViewportScrollableArea()->GetScrollOffset().Width();
return AdjustScrollForAbsoluteZoom(viewport_x, GetFrame()->PageZoomFactor());
}
double LocalDOMWindow::scrollY() const {
if (!GetFrame() || !GetFrame()->GetPage())
return 0;
LocalFrameView* view = GetFrame()->View();
if (!view)
return 0;
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
// TODO(bokan): This is wrong when the document.rootScroller is non-default.
// crbug.com/505516.
double viewport_y =
view->LayoutViewportScrollableArea()->GetScrollOffset().Height();
return AdjustScrollForAbsoluteZoom(viewport_y, GetFrame()->PageZoomFactor());
}
DOMVisualViewport* LocalDOMWindow::visualViewport() {
return visualViewport_;
}
const AtomicString& LocalDOMWindow::name() const {
if (!IsCurrentlyDisplayedInFrame())
return g_null_atom;
return GetFrame()->Tree().GetName();
}
void LocalDOMWindow::setName(const AtomicString& name) {
if (!IsCurrentlyDisplayedInFrame())
return;
GetFrame()->Tree().SetName(name, FrameTree::kReplicate);
}
void LocalDOMWindow::setStatus(const String& string) {
status_ = string;
}
void LocalDOMWindow::setDefaultStatus(const String& string) {
default_status_ = string;
}
String LocalDOMWindow::origin() const {
return GetExecutionContext()->GetSecurityOrigin()->ToString();
}
Document* LocalDOMWindow::document() const {
return document_.Get();
}
StyleMedia* LocalDOMWindow::styleMedia() const {
if (!media_)
media_ = StyleMedia::Create(GetFrame());
return media_.Get();
}
CSSStyleDeclaration* LocalDOMWindow::getComputedStyle(
Element* elt,
const String& pseudo_elt) const {
DCHECK(elt);
return CSSComputedStyleDeclaration::Create(elt, false, pseudo_elt);
}
CSSRuleList* LocalDOMWindow::getMatchedCSSRules(
Element* element,
const String& pseudo_element) const {
if (!element)
return nullptr;
if (!IsCurrentlyDisplayedInFrame())
return nullptr;
unsigned colon_start =
pseudo_element[0] == ':' ? (pseudo_element[1] == ':' ? 2 : 1) : 0;
CSSSelector::PseudoType pseudo_type = CSSSelector::ParsePseudoType(
AtomicString(pseudo_element.Substring(colon_start)), false);
if (pseudo_type == CSSSelector::kPseudoUnknown && !pseudo_element.IsEmpty())
return nullptr;
unsigned rules_to_include = StyleResolver::kAuthorCSSRules;
PseudoId pseudo_id = CSSSelector::GetPseudoId(pseudo_type);
element->GetDocument().UpdateStyleAndLayoutTree();
return document()->EnsureStyleResolver().PseudoCSSRulesForElement(
element, pseudo_id, rules_to_include);
}
double LocalDOMWindow::devicePixelRatio() const {
if (!GetFrame())
return 0.0;
return GetFrame()->DevicePixelRatio();
}
void LocalDOMWindow::scrollBy(double x,
double y,
ScrollBehavior scroll_behavior) const {
if (!IsCurrentlyDisplayedInFrame())
return;
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
LocalFrameView* view = GetFrame()->View();
if (!view)
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
x = ScrollableArea::NormalizeNonFiniteScroll(x);
y = ScrollableArea::NormalizeNonFiniteScroll(y);
ScrollableArea* viewport = view->LayoutViewportScrollableArea();
ScrollOffset current_offset = viewport->GetScrollOffset();
ScrollOffset scaled_delta(x * GetFrame()->PageZoomFactor(),
y * GetFrame()->PageZoomFactor());
viewport->SetScrollOffset(current_offset + scaled_delta, kProgrammaticScroll,
scroll_behavior);
}
void LocalDOMWindow::scrollBy(const ScrollToOptions& scroll_to_options) const {
double x = 0.0;
double y = 0.0;
if (scroll_to_options.hasLeft())
x = scroll_to_options.left();
if (scroll_to_options.hasTop())
y = scroll_to_options.top();
ScrollBehavior scroll_behavior = kScrollBehaviorAuto;
ScrollableArea::ScrollBehaviorFromString(scroll_to_options.behavior(),
scroll_behavior);
scrollBy(x, y, scroll_behavior);
}
void LocalDOMWindow::scrollTo(double x, double y) const {
if (!IsCurrentlyDisplayedInFrame())
return;
LocalFrameView* view = GetFrame()->View();
if (!view)
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
x = ScrollableArea::NormalizeNonFiniteScroll(x);
y = ScrollableArea::NormalizeNonFiniteScroll(y);
// It is only necessary to have an up-to-date layout if the position may be
// clamped, which is never the case for (0, 0).
if (x || y)
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
ScrollOffset layout_offset(x * GetFrame()->PageZoomFactor(),
y * GetFrame()->PageZoomFactor());
ScrollableArea* viewport = view->LayoutViewportScrollableArea();
viewport->SetScrollOffset(layout_offset, kProgrammaticScroll,
kScrollBehaviorAuto);
}
void LocalDOMWindow::scrollTo(const ScrollToOptions& scroll_to_options) const {
if (!IsCurrentlyDisplayedInFrame())
return;
LocalFrameView* view = GetFrame()->View();
if (!view)
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
// It is only necessary to have an up-to-date layout if the position may be
// clamped, which is never the case for (0, 0).
if (!scroll_to_options.hasLeft() || !scroll_to_options.hasTop() ||
scroll_to_options.left() || scroll_to_options.top()) {
document()->UpdateStyleAndLayoutIgnorePendingStylesheets();
}
double scaled_x = 0.0;
double scaled_y = 0.0;
ScrollableArea* viewport = view->LayoutViewportScrollableArea();
ScrollOffset current_offset = viewport->GetScrollOffset();
scaled_x = current_offset.Width();
scaled_y = current_offset.Height();
if (scroll_to_options.hasLeft())
scaled_x =
ScrollableArea::NormalizeNonFiniteScroll(scroll_to_options.left()) *
GetFrame()->PageZoomFactor();
if (scroll_to_options.hasTop())
scaled_y =
ScrollableArea::NormalizeNonFiniteScroll(scroll_to_options.top()) *
GetFrame()->PageZoomFactor();
ScrollBehavior scroll_behavior = kScrollBehaviorAuto;
ScrollableArea::ScrollBehaviorFromString(scroll_to_options.behavior(),
scroll_behavior);
viewport->SetScrollOffset(ScrollOffset(scaled_x, scaled_y),
kProgrammaticScroll, scroll_behavior);
}
void LocalDOMWindow::moveBy(int x, int y) const {
if (!GetFrame() || !GetFrame()->IsMainFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
IntRect window_rect = page->GetChromeClient().RootWindowRect();
window_rect.SaturatedMove(x, y);
// Security check (the spec talks about UniversalBrowserWrite to disable this
// check...)
page->GetChromeClient().SetWindowRectWithAdjustment(window_rect, *GetFrame());
}
void LocalDOMWindow::moveTo(int x, int y) const {
if (!GetFrame() || !GetFrame()->IsMainFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
IntRect window_rect = page->GetChromeClient().RootWindowRect();
window_rect.SetLocation(IntPoint(x, y));
// Security check (the spec talks about UniversalBrowserWrite to disable this
// check...)
page->GetChromeClient().SetWindowRectWithAdjustment(window_rect, *GetFrame());
}
void LocalDOMWindow::resizeBy(int x, int y) const {
if (!GetFrame() || !GetFrame()->IsMainFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
IntRect fr = page->GetChromeClient().RootWindowRect();
IntSize dest = fr.Size() + IntSize(x, y);
IntRect update(fr.Location(), dest);
page->GetChromeClient().SetWindowRectWithAdjustment(update, *GetFrame());
}
void LocalDOMWindow::resizeTo(int width, int height) const {
if (!GetFrame() || !GetFrame()->IsMainFrame())
return;
Page* page = GetFrame()->GetPage();
if (!page)
return;
IntRect fr = page->GetChromeClient().RootWindowRect();
IntSize dest = IntSize(width, height);
IntRect update(fr.Location(), dest);
page->GetChromeClient().SetWindowRectWithAdjustment(update, *GetFrame());
}
int LocalDOMWindow::requestAnimationFrame(V8FrameRequestCallback* callback) {
FrameRequestCallbackCollection::V8FrameCallback* frame_callback =
FrameRequestCallbackCollection::V8FrameCallback::Create(callback);
frame_callback->SetUseLegacyTimeBase(false);
if (Document* doc = document())
return doc->RequestAnimationFrame(frame_callback);
return 0;
}
int LocalDOMWindow::webkitRequestAnimationFrame(
V8FrameRequestCallback* callback) {
FrameRequestCallbackCollection::V8FrameCallback* frame_callback =
FrameRequestCallbackCollection::V8FrameCallback::Create(callback);
frame_callback->SetUseLegacyTimeBase(true);
if (Document* document = this->document())
return document->RequestAnimationFrame(frame_callback);
return 0;
}
void LocalDOMWindow::cancelAnimationFrame(int id) {
if (Document* document = this->document())
document->CancelAnimationFrame(id);
}
int LocalDOMWindow::requestIdleCallback(V8IdleRequestCallback* callback,
const IdleRequestOptions& options) {
if (Document* document = this->document()) {
return document->RequestIdleCallback(
ScriptedIdleTaskController::V8IdleTask::Create(callback), options);
}
return 0;
}
void LocalDOMWindow::cancelIdleCallback(int id) {
if (Document* document = this->document())
document->CancelIdleCallback(id);
}
CustomElementRegistry* LocalDOMWindow::customElements(
ScriptState* script_state) const {
if (!script_state->World().IsMainWorld())
return nullptr;
return customElements();
}
CustomElementRegistry* LocalDOMWindow::customElements() const {
if (!custom_elements_ && document_)
custom_elements_ = CustomElementRegistry::Create(this);
return custom_elements_;
}
CustomElementRegistry* LocalDOMWindow::MaybeCustomElements() const {
return custom_elements_;
}
void LocalDOMWindow::SetModulator(Modulator* modulator) {
DCHECK(!modulator_);
modulator_ = modulator;
}
External* LocalDOMWindow::external() {
if (!external_)
external_ = new External;
return external_;
}
bool LocalDOMWindow::isSecureContext() const {
if (!GetFrame())
return false;
return document()->IsSecureContext();
}
void LocalDOMWindow::AddedEventListener(
const AtomicString& event_type,
RegisteredEventListener& registered_listener) {
DOMWindow::AddedEventListener(event_type, registered_listener);
if (GetFrame() && GetFrame()->GetPage())
GetFrame()->GetPage()->GetEventHandlerRegistry().DidAddEventHandler(
*this, event_type, registered_listener.Options());
if (Document* document = this->document())
document->AddListenerTypeIfNeeded(event_type, *this);
for (auto& it : event_listener_observers_) {
it->DidAddEventListener(this, event_type);
}
if (event_type == EventTypeNames::unload) {
UseCounter::Count(document(), WebFeature::kDocumentUnloadRegistered);
TrackUnloadEventListener(this);
} else if (event_type == EventTypeNames::beforeunload) {
UseCounter::Count(document(), WebFeature::kDocumentBeforeUnloadRegistered);
TrackBeforeUnloadEventListener(this);
if (GetFrame() && !GetFrame()->IsMainFrame()) {
UseCounter::Count(document(),
WebFeature::kSubFrameBeforeUnloadRegistered);
}
}
}
void LocalDOMWindow::RemovedEventListener(
const AtomicString& event_type,
const RegisteredEventListener& registered_listener) {
DOMWindow::RemovedEventListener(event_type, registered_listener);
if (GetFrame() && GetFrame()->GetPage())
GetFrame()->GetPage()->GetEventHandlerRegistry().DidRemoveEventHandler(
*this, event_type, registered_listener.Options());
for (auto& it : event_listener_observers_) {
it->DidRemoveEventListener(this, event_type);
}
if (event_type == EventTypeNames::unload) {
UntrackUnloadEventListener(this);
} else if (event_type == EventTypeNames::beforeunload) {
UntrackBeforeUnloadEventListener(this);
}
}
void LocalDOMWindow::WarnUnusedPreloads(TimerBase* base) {
if (GetFrame() && GetFrame()->Loader().GetDocumentLoader()) {
ResourceFetcher* fetcher =
GetFrame()->Loader().GetDocumentLoader()->Fetcher();
DCHECK(fetcher);
if (fetcher->CountPreloads())
fetcher->WarnUnusedPreloads();
}
}
void LocalDOMWindow::DispatchLoadEvent() {
Event* load_event(Event::Create(EventTypeNames::load));
if (GetFrame() && GetFrame()->Loader().GetDocumentLoader() &&
!GetFrame()->Loader().GetDocumentLoader()->GetTiming().LoadEventStart()) {
DocumentLoader* document_loader = GetFrame()->Loader().GetDocumentLoader();
DocumentLoadTiming& timing = document_loader->GetTiming();
timing.MarkLoadEventStart();
DispatchEvent(load_event, document());
timing.MarkLoadEventEnd();
DCHECK(document_loader->Fetcher());
// If fetcher->countPreloads() is not empty here, it's full of link
// preloads, as speculatove preloads were cleared at DCL.
if (GetFrame() &&
document_loader == GetFrame()->Loader().GetDocumentLoader() &&
document_loader->Fetcher()->CountPreloads())
unused_preloads_timer_.StartOneShot(kUnusedPreloadTimeoutInSeconds,
BLINK_FROM_HERE);
} else {
DispatchEvent(load_event, document());
}
if (GetFrame()) {
Performance* performance = DOMWindowPerformance::performance(*this);
DCHECK(performance);
performance->NotifyNavigationTimingToObservers();
}
// For load events, send a separate load event to the enclosing frame only.
// This is a DOM extension and is independent of bubbling/capturing rules of
// the DOM.
FrameOwner* owner = GetFrame() ? GetFrame()->Owner() : nullptr;
if (owner)
owner->DispatchLoad();
TRACE_EVENT_INSTANT1("devtools.timeline", "MarkLoad",
TRACE_EVENT_SCOPE_THREAD, "data",
InspectorMarkLoadEvent::Data(GetFrame()));
probe::loadEventFired(GetFrame());
}
DispatchEventResult LocalDOMWindow::DispatchEvent(Event* event,
EventTarget* target) {
#if DCHECK_IS_ON()
DCHECK(!EventDispatchForbiddenScope::IsEventDispatchForbidden());
#endif
event->SetTrusted(true);
event->SetTarget(target ? target : this);
event->SetCurrentTarget(this);
event->SetEventPhase(Event::kAtTarget);
TRACE_EVENT1("devtools.timeline", "EventDispatch", "data",
InspectorEventDispatchEvent::Data(*event));
DispatchEventResult result;
if (GetFrame() && GetFrame()->IsMainFrame() &&
event->type() == EventTypeNames::resize) {
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.EventListenerDuration.Resize");
result = FireEventListeners(event);
} else {
result = FireEventListeners(event);
}
return result;
}
void LocalDOMWindow::RemoveAllEventListeners() {
EventTarget::RemoveAllEventListeners();
for (auto& it : event_listener_observers_) {
it->DidRemoveAllEventListeners(this);
}
if (GetFrame() && GetFrame()->GetPage())
GetFrame()->GetPage()->GetEventHandlerRegistry().DidRemoveAllEventHandlers(
*this);
UntrackAllUnloadEventListeners(this);
UntrackAllBeforeUnloadEventListeners(this);
}
void LocalDOMWindow::FinishedLoading() {
if (should_print_when_finished_loading_) {
should_print_when_finished_loading_ = false;
print(nullptr);
}
}
void LocalDOMWindow::PrintErrorMessage(const String& message) const {
if (!IsCurrentlyDisplayedInFrame())
return;
if (message.IsEmpty())
return;
GetFrameConsole()->AddMessage(
ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, message));
}
DOMWindow* LocalDOMWindow::open(const String& url_string,
const AtomicString& frame_name,
const String& window_features_string,
LocalDOMWindow* calling_window,
LocalDOMWindow* entered_window,
ExceptionState& exception_state) {
if (!IsCurrentlyDisplayedInFrame())
return nullptr;
if (!calling_window->GetFrame())
return nullptr;
Document* active_document = calling_window->document();
if (!active_document)
return nullptr;
LocalFrame* first_frame = entered_window->GetFrame();
if (!first_frame)
return nullptr;
UseCounter::Count(*active_document, WebFeature::kDOMWindowOpen);
if (!window_features_string.IsEmpty())
UseCounter::Count(*active_document, WebFeature::kDOMWindowOpenFeatures);
probe::windowOpen(first_frame->GetDocument(), url_string, frame_name,
window_features_string,
UserGestureIndicator::ProcessingUserGesture());
// Get the target frame for the special cases of _top and _parent.
// In those cases, we schedule a location change right now and return early.
Frame* target_frame = nullptr;
if (EqualIgnoringASCIICase(frame_name, "_top")) {
target_frame = &GetFrame()->Tree().Top();
} else if (EqualIgnoringASCIICase(frame_name, "_parent")) {
if (Frame* parent = GetFrame()->Tree().Parent())
target_frame = parent;
else
target_frame = GetFrame();
}
if (target_frame) {
if (!active_document->GetFrame() ||
!active_document->GetFrame()->CanNavigate(*target_frame)) {
return nullptr;
}
KURL completed_url = first_frame->GetDocument()->CompleteURL(url_string);
if (target_frame->DomWindow()->IsInsecureScriptAccess(*calling_window,
completed_url))
return target_frame->DomWindow();
if (url_string.IsEmpty())
return target_frame->DomWindow();
target_frame->Navigate(*active_document, completed_url, false,
UserGestureStatus::kNone);
return target_frame->DomWindow();
}
DOMWindow* new_window =
CreateWindow(url_string, frame_name, window_features_string,
*calling_window, *first_frame, *GetFrame(), exception_state);
return new_window;
}
void LocalDOMWindow::Trace(blink::Visitor* visitor) {
visitor->Trace(document_);
visitor->Trace(screen_);
visitor->Trace(history_);
visitor->Trace(locationbar_);
visitor->Trace(menubar_);
visitor->Trace(personalbar_);
visitor->Trace(scrollbars_);
visitor->Trace(statusbar_);
visitor->Trace(toolbar_);
visitor->Trace(navigator_);
visitor->Trace(media_);
visitor->Trace(custom_elements_);
visitor->Trace(modulator_);
visitor->Trace(external_);
visitor->Trace(application_cache_);
visitor->Trace(event_queue_);
visitor->Trace(post_message_timers_);
visitor->Trace(visualViewport_);
visitor->Trace(event_listener_observers_);
DOMWindow::Trace(visitor);
Supplementable<LocalDOMWindow>::Trace(visitor);
}
void LocalDOMWindow::TraceWrappers(
const ScriptWrappableVisitor* visitor) const {
visitor->TraceWrappers(custom_elements_);
visitor->TraceWrappers(document_);
visitor->TraceWrappers(modulator_);
visitor->TraceWrappers(navigator_);
DOMWindow::TraceWrappers(visitor);
}
} // namespace blink