LibWeb: Add the History object and stub pushState and replaceState

The spec allows us to optionally return from these for any reason.
Our reason is that we don't have all the infrastructure in place yet to
implement them.
This commit is contained in:
Luke Wilde 2021-09-11 23:43:34 +01:00 committed by Andreas Kling
parent 3faed65e2b
commit 1927600852
Notes: sideshowbarker 2024-07-18 04:12:33 +09:00
10 changed files with 127 additions and 0 deletions

View file

@ -19,6 +19,7 @@
#include <LibWeb/Bindings/EventTargetPrototype.h>
#include <LibWeb/Bindings/EventWrapper.h>
#include <LibWeb/Bindings/EventWrapperFactory.h>
#include <LibWeb/Bindings/HistoryWrapper.h>
#include <LibWeb/Bindings/LocationObject.h>
#include <LibWeb/Bindings/NavigatorObject.h>
#include <LibWeb/Bindings/NodeWrapperFactory.h>
@ -57,6 +58,7 @@ void WindowObject::initialize_global_object()
define_native_accessor("top", top_getter, nullptr, JS::Attribute::Enumerable);
define_native_accessor("parent", parent_getter, {}, JS::Attribute::Enumerable);
define_native_accessor("document", document_getter, {}, JS::Attribute::Enumerable);
define_native_accessor("history", history_getter, {}, JS::Attribute::Enumerable);
define_native_accessor("performance", performance_getter, {}, JS::Attribute::Enumerable);
define_native_accessor("screen", screen_getter, {}, JS::Attribute::Enumerable);
define_native_accessor("innerWidth", inner_width_getter, {}, JS::Attribute::Enumerable);
@ -640,4 +642,12 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::scroll_by)
return JS::js_undefined();
}
JS_DEFINE_NATIVE_FUNCTION(WindowObject::history_getter)
{
auto* impl = impl_from(vm, global_object);
if (!impl)
return {};
return wrap(global_object, impl->associated_document().history());
}
}

View file

@ -64,6 +64,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(document_getter);
JS_DECLARE_NATIVE_FUNCTION(performance_getter);
JS_DECLARE_NATIVE_FUNCTION(history_getter);
JS_DECLARE_NATIVE_FUNCTION(screen_getter);
JS_DECLARE_NATIVE_FUNCTION(event_getter);

View file

@ -182,6 +182,8 @@
#include <LibWeb/Bindings/HTMLUnknownElementPrototype.h>
#include <LibWeb/Bindings/HTMLVideoElementConstructor.h>
#include <LibWeb/Bindings/HTMLVideoElementPrototype.h>
#include <LibWeb/Bindings/HistoryConstructor.h>
#include <LibWeb/Bindings/HistoryPrototype.h>
#include <LibWeb/Bindings/ImageConstructor.h>
#include <LibWeb/Bindings/ImageDataConstructor.h>
#include <LibWeb/Bindings/ImageDataPrototype.h>
@ -259,6 +261,7 @@
ADD_WINDOW_OBJECT_INTERFACE(Element) \
ADD_WINDOW_OBJECT_INTERFACE(Event) \
ADD_WINDOW_OBJECT_INTERFACE(EventTarget) \
ADD_WINDOW_OBJECT_INTERFACE(History) \
ADD_WINDOW_OBJECT_INTERFACE(HTMLAnchorElement) \
ADD_WINDOW_OBJECT_INTERFACE(HTMLAreaElement) \
ADD_WINDOW_OBJECT_INTERFACE(HTMLAudioElement) \

View file

@ -77,6 +77,7 @@ set(SOURCES
HTML/EventNames.cpp
HTML/FormAssociatedElement.cpp
HTML/GlobalEventHandlers.cpp
HTML/History.cpp
HTML/HTMLAnchorElement.cpp
HTML/HTMLAreaElement.cpp
HTML/HTMLAudioElement.cpp
@ -329,6 +330,7 @@ libweb_js_wrapper(DOM/Text)
libweb_js_wrapper(HTML/CanvasRenderingContext2D)
libweb_js_wrapper(HTML/CloseEvent)
libweb_js_wrapper(HTML/DOMParser)
libweb_js_wrapper(HTML/History)
libweb_js_wrapper(HTML/HTMLAnchorElement)
libweb_js_wrapper(HTML/HTMLAreaElement)
libweb_js_wrapper(HTML/HTMLAudioElement)

View file

@ -64,6 +64,7 @@ Document::Document(const URL& url)
, m_url(url)
, m_window(Window::create_with_document(*this))
, m_implementation(DOMImplementation::create(*this))
, m_history(HTML::History::create(*this))
{
m_style_update_timer = Core::Timer::create_single_shot(0, [this] {
update_style();

View file

@ -27,6 +27,7 @@
#include <LibWeb/DOM/NonElementParentNode.h>
#include <LibWeb/DOM/ParentNode.h>
#include <LibWeb/HTML/HTMLScriptElement.h>
#include <LibWeb/HTML/History.h>
namespace Web::DOM {
@ -276,6 +277,8 @@ public:
bool is_fully_active() const;
NonnullRefPtr<HTML::History> history() const { return m_history; }
private:
explicit Document(const URL&);
@ -356,6 +359,8 @@ private:
// https://html.spec.whatwg.org/multipage/semantics.html#script-blocking-style-sheet-counter
u32 m_script_blocking_style_sheet_counter { 0 };
NonnullRefPtr<HTML::History> m_history;
};
}

View file

@ -223,6 +223,7 @@ class ElementWrapper;
class EventListenerWrapper;
class EventTargetWrapper;
class EventWrapper;
class HistoryWrapper;
class HTMLAnchorElementWrapper;
class HTMLAreaElementWrapper;
class HTMLAudioElementWrapper;

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/History.h>
namespace Web::HTML {
History::History(DOM::Document& document)
: m_associated_document(document)
{
}
History::~History()
{
}
// https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
DOM::ExceptionOr<void> History::push_state(JS::Value data, String const&, String const& url)
{
// NOTE: The second parameter of this function is intentionally unused.
return shared_history_push_replace_state(data, url, IsPush::Yes);
}
// https://html.spec.whatwg.org/multipage/history.html#dom-history-replacestate
DOM::ExceptionOr<void> History::replace_state(JS::Value data, String const&, String const& url)
{
// NOTE: The second parameter of this function is intentionally unused.
return shared_history_push_replace_state(data, url, IsPush::No);
}
// https://html.spec.whatwg.org/multipage/history.html#shared-history-push/replace-state-steps
DOM::ExceptionOr<void> History::shared_history_push_replace_state(JS::Value, String const&, IsPush)
{
// 1. Let document be history's associated Document. (NOTE: Not necessary)
// 2. If document is not fully active, then throw a "SecurityError" DOMException.
if (!m_associated_document.is_fully_active())
return DOM::SecurityError::create("Cannot perform pushState or replaceState on a document that isn't fully active.");
// 3. Optionally, return. (For example, the user agent might disallow calls to these methods that are invoked on a timer,
// or from event listeners that are not triggered in response to a clear user action, or that are invoked in rapid succession.)
dbgln("FIXME: Implement shared_history_push_replace_state.");
return {};
// FIXME: Add the rest of the spec steps once they're added.
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <AK/Weakable.h>
#include <LibJS/Heap/Handle.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/Forward.h>
namespace Web::HTML {
class History final
: public RefCounted<History>
, public Weakable<History>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::HistoryWrapper;
static NonnullRefPtr<History> create(DOM::Document& document)
{
return adopt_ref(*new History(document));
}
virtual ~History() override;
DOM::ExceptionOr<void> push_state(JS::Value data, String const& unused, String const& url);
DOM::ExceptionOr<void> replace_state(JS::Value data, String const& unused, String const& url);
private:
explicit History(DOM::Document&);
enum class IsPush {
No,
Yes,
};
DOM::ExceptionOr<void> shared_history_push_replace_state(JS::Value data, String const& url, IsPush is_push);
DOM::Document& m_associated_document;
};
}

View file

@ -0,0 +1,5 @@
[Exposed=Window]
interface History {
undefined pushState(any data, DOMString unused, optional USVString? url = null);
undefined replaceState(any data, DOMString unused, optional USVString? url = null);
};