PopStateEvent.state should use the same object as history.state

https://bugs.webkit.org/show_bug.cgi?id=77493

Patch by Pablo Flouret <pablof@motorola.com> on 2012-02-22
Reviewed by Kentaro Hara.

Source/WebCore:

Tests: fast/loader/stateobjects/state-attribute-history-getter.html
       fast/loader/stateobjects/state-attribute-popstate-event.html

* bindings/js/JSPopStateEventCustom.cpp:
(WebCore):
(WebCore::cacheState):
(WebCore::JSPopStateEvent::state):
* bindings/v8/V8HiddenPropertyName.h:
(WebCore):
* bindings/v8/custom/V8HistoryCustom.cpp:
(WebCore::V8History::stateAccessorGetter):
(WebCore::V8History::pushStateCallback):
(WebCore::V8History::replaceStateCallback):
* bindings/v8/custom/V8PopStateEventCustom.cpp:
(WebCore):
(WebCore::cacheState):
(WebCore::V8PopStateEvent::stateAccessorGetter):
* dom/Document.cpp:
(WebCore::Document::enqueuePopstateEvent):
* dom/PopStateEvent.cpp:
(WebCore::PopStateEvent::PopStateEvent):
(WebCore::PopStateEvent::create):
* dom/PopStateEvent.h:
(WebCore):
(PopStateEvent):
(WebCore::PopStateEvent::history):
* dom/PopStateEvent.idl:
* page/History.cpp:
(WebCore::History::stateChanged):
(WebCore):
(WebCore::History::isSameAsCurrentState):
* page/History.h:
(History):

LayoutTests:

* fast/loader/stateobjects/state-attribute-history-getter-expected.txt: Added.
* fast/loader/stateobjects/state-attribute-history-getter.html: Added.
* fast/loader/stateobjects/state-attribute-only-one-deserialization-expected.txt:
* fast/loader/stateobjects/state-attribute-only-one-deserialization.html:
* fast/loader/stateobjects/state-attribute-popstate-event-expected.txt: Added.
* fast/loader/stateobjects/state-attribute-popstate-event.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108596 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 51e62c4f
2012-02-22 Pablo Flouret <pablof@motorola.com>
PopStateEvent.state should use the same object as history.state
https://bugs.webkit.org/show_bug.cgi?id=77493
Reviewed by Kentaro Hara.
* fast/loader/stateobjects/state-attribute-history-getter-expected.txt: Added.
* fast/loader/stateobjects/state-attribute-history-getter.html: Added.
* fast/loader/stateobjects/state-attribute-only-one-deserialization-expected.txt:
* fast/loader/stateobjects/state-attribute-only-one-deserialization.html:
* fast/loader/stateobjects/state-attribute-popstate-event-expected.txt: Added.
* fast/loader/stateobjects/state-attribute-popstate-event.html: Added.
2012-02-22 Dmitry Lomov <dslomov@google.com>
[JSC] Implement ArrayBuffer and typed array cloning in JSC
Check that setting a custom getter for history.state works correctly and that PopStateEvent.state still has a correct value.
PASS
<!DOCTYPE html>
<p>Check that setting a custom getter for history.state works correctly and that PopStateEvent.state still has a correct value.</p>
<pre id=log></pre>
<script>
function log(msg) {
document.querySelector("#log").innerHTML += msg + "<br>";
}
if (window.layoutTestController) {
layoutTestController.clearBackForwardList();
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function test() {
if (!("state" in history)) {
log("FAIL: history.state is not defined");
return;
}
try {
Object.defineProperty(history, "state", { get: function () { return "oh hai" } });
} catch (e) {
// history.state is not configurable in JSC.
log(e.name == "TypeError" ? "PASS" : ("FAIL: unexpected exception: " + e));
layoutTestController.notifyDone();
return;
}
if (history.state !== "oh hai") {
log('FAIL: history.state != "oh hai"');
}
history.pushState(42, "", "");
history.pushState(43, "", "");
window.onpopstate = function(e) {
if (e.state !== 42)
log("FAIL: e.state expected 42, was " + e.state + " (of type " + typeof e.state + ")");
else
log("PASS");
if (window.layoutTestController)
layoutTestController.notifyDone();
}
history.back();
}
test();
</script>
......@@ -12,7 +12,7 @@ Inside popstate event
PASS history.state !== stateObject is true
PASS popStateEvent.state !== stateObject is true
FAIL popStateEvent.state === history.state should be true. Was false.
PASS popStateEvent.state === history.state is true
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -45,7 +45,7 @@
// Our stored reference to stateObject will not match the state object in this pop state event, as it is the same as history.state which is a structured clone of the history item's state object.
shouldBeTrue("popStateEvent.state !== stateObject");
// The event's state object and the current state object should match.
shouldBeTrue("popStateEvent.state === history.state"); // This fails for now, needs to be fixed.
shouldBeTrue("popStateEvent.state === history.state");
var s = document.createElement("script");
s.src = "../../js/resources/js-test-post.js";
......
Check that PopStateEvent.state always has a correct value.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS history.state is defined.
Push state 1
Push state 2
PASS history.state is 2
Go back
PASS popStateEvent.state is 1
PASS history.state is 1
Push state 3
PASS popStateEvent.state is 1
PASS history.state is 3
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../js/resources/js-test-pre.js"></script>
</head/>
<body>
<script>
description("Check that PopStateEvent.state always has a correct value.");
window.jsTestIsAsync = true;
if (window.layoutTestController) {
layoutTestController.clearBackForwardList();
layoutTestController.waitUntilDone();
}
shouldBeDefined("history.state");
debug("Push state 1");
history.pushState(1, "", "");
debug("Push state 2");
history.pushState(2, "", "");
shouldEvaluateTo("history.state", 2);
var popStateEvent;
window.onpopstate = function(e) {
popStateEvent = e;
shouldEvaluateTo("popStateEvent.state", 1);
shouldEvaluateTo("history.state", 1);
debug("Push state 3");
history.pushState(3, "", "");
shouldEvaluateTo("popStateEvent.state", 1);
shouldEvaluateTo("history.state", 3);
var s = document.createElement("script");
s.src = "../../js/resources/js-test-post.js";
document.body.appendChild(s);
setTimeout(finishJSTest, 0);
}
debug("Go back");
history.back();
</script>
</body>
</html>
2012-02-22 Pablo Flouret <pablof@motorola.com>
PopStateEvent.state should use the same object as history.state
https://bugs.webkit.org/show_bug.cgi?id=77493
Reviewed by Kentaro Hara.
Tests: fast/loader/stateobjects/state-attribute-history-getter.html
fast/loader/stateobjects/state-attribute-popstate-event.html
* bindings/js/JSPopStateEventCustom.cpp:
(WebCore):
(WebCore::cacheState):
(WebCore::JSPopStateEvent::state):
* bindings/v8/V8HiddenPropertyName.h:
(WebCore):
* bindings/v8/custom/V8HistoryCustom.cpp:
(WebCore::V8History::stateAccessorGetter):
(WebCore::V8History::pushStateCallback):
(WebCore::V8History::replaceStateCallback):
* bindings/v8/custom/V8PopStateEventCustom.cpp:
(WebCore):
(WebCore::cacheState):
(WebCore::V8PopStateEvent::stateAccessorGetter):
* dom/Document.cpp:
(WebCore::Document::enqueuePopstateEvent):
* dom/PopStateEvent.cpp:
(WebCore::PopStateEvent::PopStateEvent):
(WebCore::PopStateEvent::create):
* dom/PopStateEvent.h:
(WebCore):
(PopStateEvent):
(WebCore::PopStateEvent::history):
* dom/PopStateEvent.idl:
* page/History.cpp:
(WebCore::History::stateChanged):
(WebCore):
(WebCore::History::isSameAsCurrentState):
* page/History.h:
(History):
2012-02-22 Adam Klein <adamk@chromium.org>
Remove obsolete FIXMEs from ContainerNode
......@@ -30,21 +30,52 @@
#include "config.h"
#include "History.h"
#include "JSHistory.h"
#include "JSPopStateEvent.h"
using namespace JSC;
namespace WebCore {
// Save the state value to the m_state member of a JSPopStateEvent, and return it, for convenience.
static const JSValue& cacheState(ExecState* exec, JSPopStateEvent* event, const JSValue& state)
{
event->m_state.set(exec->globalData(), event, state);
return state;
}
JSValue JSPopStateEvent::state(ExecState* exec) const
{
JSValue cachedValue = m_state.get();
if (!cachedValue.isEmpty())
return cachedValue;
PopStateEvent* event = static_cast<PopStateEvent*>(impl());
SerializedScriptValue* serializedState = event->serializedState();
if (serializedState)
return serializedState->deserialize(exec, globalObject(), 0);
if (!event->state().hasNoValue())
return event->state().jsValue();
return jsNull();
return cacheState(exec, const_cast<JSPopStateEvent*>(this), event->state().jsValue());
History* history = event->history();
if (!history || !event->serializedState())
return cacheState(exec, const_cast<JSPopStateEvent*>(this), jsNull());
// There's no cached value from a previous invocation, nor a state value was provided by the
// event, but there is a history object, so first we need to see if the state object has been
// deserialized through the history object already.
// The current history state object might've changed in the meantime, so we need to take care
// of using the correct one, and always share the same deserialization with history.state.
bool isSameState = history->isSameAsCurrentState(event->serializedState());
JSValue result;
if (isSameState) {
JSHistory* jsHistory = static_cast<JSHistory*>(toJS(exec, globalObject(), history).asCell());
result = jsHistory->state(exec);
} else
result = event->serializedState()->deserialize(exec, globalObject(), 0);
return cacheState(exec, const_cast<JSPopStateEvent*>(this), result);
}
} // namespace WebCore
......@@ -43,7 +43,8 @@ namespace WebCore {
V(devtoolsInjectedScript) \
V(sleepFunction) \
V(toStringString) \
V(event)
V(event) \
V(state)
class V8HiddenPropertyName {
......
......@@ -37,6 +37,7 @@
#include "V8Binding.h"
#include "V8BindingState.h"
#include "V8DOMWindow.h"
#include "V8HiddenPropertyName.h"
#include "V8Proxy.h"
namespace WebCore {
......@@ -46,15 +47,14 @@ v8::Handle<v8::Value> V8History::stateAccessorGetter(v8::Local<v8::String> name,
INC_STATS("DOM.History.state");
History* history = V8History::toNative(info.Holder());
v8::Handle<v8::String> propertyName = v8::String::NewSymbol("state");
v8::Handle<v8::Value> value = info.Holder()->GetHiddenValue(propertyName);
v8::Handle<v8::Value> value = info.Holder()->GetHiddenValue(V8HiddenPropertyName::state());
if (!value.IsEmpty() && !history->stateChanged())
return value;
SerializedScriptValue* serialized = history->state();
value = serialized ? serialized->deserialize() : v8::Handle<v8::Value>(v8::Null());
info.Holder()->SetHiddenValue(propertyName, value);
info.Holder()->SetHiddenValue(V8HiddenPropertyName::state(), value);
return value;
}
......@@ -80,7 +80,7 @@ v8::Handle<v8::Value> V8History::pushStateCallback(const v8::Arguments& args)
ExceptionCode ec = 0;
History* history = V8History::toNative(args.Holder());
history->stateObjectAdded(historyState.release(), title, url, History::StateObjectPush, ec);
args.Holder()->DeleteHiddenValue(v8::String::NewSymbol("state"));
args.Holder()->DeleteHiddenValue(V8HiddenPropertyName::state());
return throwError(ec);
}
......@@ -105,7 +105,7 @@ v8::Handle<v8::Value> V8History::replaceStateCallback(const v8::Arguments& args)
ExceptionCode ec = 0;
History* history = V8History::toNative(args.Holder());
history->stateObjectAdded(historyState.release(), title, url, History::StateObjectReplace, ec);
args.Holder()->DeleteHiddenValue(v8::String::NewSymbol("state"));
args.Holder()->DeleteHiddenValue(V8HiddenPropertyName::state());
return throwError(ec);
}
......
......@@ -31,23 +31,60 @@
#include "config.h"
#include "V8PopStateEvent.h"
#include "History.h"
#include "PopStateEvent.h"
#include "SerializedScriptValue.h"
#include "V8HiddenPropertyName.h"
#include "V8History.h"
#include "V8Proxy.h"
namespace WebCore {
// Save the state value to a hidden attribute in the V8PopStateEvent, and return it, for convenience.
static v8::Handle<v8::Value> cacheState(v8::Handle<v8::Object> popStateEvent, v8::Handle<v8::Value> state)
{
popStateEvent->SetHiddenValue(V8HiddenPropertyName::state(), state);
return state;
}
v8::Handle<v8::Value> V8PopStateEvent::stateAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
INC_STATS("DOM.PopStateEvent.state");
v8::Handle<v8::Value> result = info.Holder()->GetHiddenValue(V8HiddenPropertyName::state());
if (!result.IsEmpty())
return result;
PopStateEvent* event = V8PopStateEvent::toNative(info.Holder());
SerializedScriptValue* serializedState = event->serializedState();
if (serializedState)
return serializedState->deserialize();
if (!event->state().hasNoValue())
return event->state().v8Value();
return v8::Null();
return cacheState(info.Holder(), event->state().v8Value());
History* history = event->history();
if (!history || !event->serializedState())
return cacheState(info.Holder(), v8::Null());
// There's no cached value from a previous invocation, nor a state value was provided by the
// event, but there is a history object, so first we need to see if the state object has been
// deserialized through the history object already.
// The current history state object might've changed in the meantime, so we need to take care
// of using the correct one, and always share the same deserialization with history.state.
bool isSameState = history->isSameAsCurrentState(event->serializedState());
if (isSameState) {
v8::Handle<v8::Object> v8History = toV8(history).As<v8::Object>();
if (!history->stateChanged()) {
result = v8History->GetHiddenValue(V8HiddenPropertyName::state());
if (!result.IsEmpty())
return cacheState(info.Holder(), result);
}
result = event->serializedState()->deserialize();
v8History->SetHiddenValue(V8HiddenPropertyName::state(), result);
} else
result = event->serializedState()->deserialize();
return cacheState(info.Holder(), result);
}
} // namespace WebCore
......@@ -79,6 +79,7 @@
#include "GeolocationController.h"
#include "HashChangeEvent.h"
#include "HistogramSupport.h"
#include "History.h"
#include "HTMLAllCollection.h"
#include "HTMLAnchorElement.h"
#include "HTMLBodyElement.h"
......@@ -5027,7 +5028,7 @@ void Document::enqueueHashchangeEvent(const String& oldURL, const String& newURL
void Document::enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject)
{
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=36202 Popstate event needs to fire asynchronously
dispatchWindowEvent(PopStateEvent::create(stateObject));
dispatchWindowEvent(PopStateEvent::create(stateObject, domWindow() ? domWindow()->history() : 0));
}
void Document::addMediaCanStartListener(MediaCanStartListener* listener)
......
......@@ -28,6 +28,8 @@
#include "PopStateEvent.h"
#include "EventNames.h"
#include "History.h"
#include "SerializedScriptValue.h"
namespace WebCore {
......@@ -38,6 +40,7 @@ PopStateEventInit::PopStateEventInit()
PopStateEvent::PopStateEvent()
: Event(eventNames().popstateEvent, false, true)
, m_serializedState(0)
, m_history(0)
{
}
......@@ -45,19 +48,14 @@ PopStateEvent::PopStateEvent(const AtomicString& type, const PopStateEventInit&
: Event(type, initializer)
, m_state(initializer.state)
, m_serializedState(0)
, m_history(0)
{
}
PopStateEvent::PopStateEvent(const ScriptValue& state)
: Event(eventNames().popstateEvent, false, true)
, m_state(state)
, m_serializedState(0)
{
}
PopStateEvent::PopStateEvent(PassRefPtr<SerializedScriptValue> serializedState)
PopStateEvent::PopStateEvent(PassRefPtr<SerializedScriptValue> serializedState, PassRefPtr<History> history)
: Event(eventNames().popstateEvent, false, true)
, m_serializedState(serializedState)
, m_history(history)
{
}
......@@ -70,14 +68,9 @@ PassRefPtr<PopStateEvent> PopStateEvent::create()
return adoptRef(new PopStateEvent);
}
PassRefPtr<PopStateEvent> PopStateEvent::create(const ScriptValue& state)
{
return adoptRef(new PopStateEvent(state));
}
PassRefPtr<PopStateEvent> PopStateEvent::create(PassRefPtr<SerializedScriptValue> serializedState)
PassRefPtr<PopStateEvent> PopStateEvent::create(PassRefPtr<SerializedScriptValue> serializedState, PassRefPtr<History> history)
{
return adoptRef(new PopStateEvent(serializedState));
return adoptRef(new PopStateEvent(serializedState, history));
}
PassRefPtr<PopStateEvent> PopStateEvent::create(const AtomicString& type, const PopStateEventInit& initializer)
......
......@@ -29,7 +29,6 @@
#include "Event.h"
#include "ScriptValue.h"
#include "SerializedScriptValue.h"
namespace WebCore {
......@@ -39,27 +38,30 @@ struct PopStateEventInit : public EventInit {
ScriptValue state;
};
class History;
class SerializedScriptValue;
class PopStateEvent : public Event {
public:
virtual ~PopStateEvent();
static PassRefPtr<PopStateEvent> create();
static PassRefPtr<PopStateEvent> create(const ScriptValue&);
static PassRefPtr<PopStateEvent> create(PassRefPtr<SerializedScriptValue>);
static PassRefPtr<PopStateEvent> create(PassRefPtr<SerializedScriptValue>, PassRefPtr<History>);
static PassRefPtr<PopStateEvent> create(const AtomicString&, const PopStateEventInit&);
SerializedScriptValue* serializedState() const { return m_serializedState.get(); }
ScriptValue state() const { return m_state; }
History* history() const { return m_history.get(); }
virtual const AtomicString& interfaceName() const;
private:
PopStateEvent();
PopStateEvent(const AtomicString&, const PopStateEventInit&);
explicit PopStateEvent(const ScriptValue&);
explicit PopStateEvent(PassRefPtr<SerializedScriptValue>);
explicit PopStateEvent(PassRefPtr<SerializedScriptValue>, PassRefPtr<History>);
ScriptValue m_state;
RefPtr<SerializedScriptValue> m_serializedState;
RefPtr<History> m_history;
};
} // namespace WebCore
......
......@@ -30,7 +30,7 @@ module events {
interface [
ConstructorTemplate=Event
] PopStateEvent : Event {
readonly attribute [InitializedByEventConstructor, CustomGetter] DOMObject state;
readonly attribute [InitializedByEventConstructor, CachedAttribute, CustomGetter] DOMObject state;
};
#endif
......
......@@ -77,6 +77,11 @@ bool History::stateChanged() const
return m_lastStateObjectRequested != stateInternal();
}
bool History::isSameAsCurrentState(SerializedScriptValue* state) const
{
return state == stateInternal();
}
void History::back()
{
go(-1);
......
......@@ -45,7 +45,6 @@ public:
unsigned length() const;
SerializedScriptValue* state();
bool stateChanged() const;
void back();
void forward();
void go(int distance);
......@@ -54,6 +53,9 @@ public:
void forward(ScriptExecutionContext*);
void go(ScriptExecutionContext*, int distance);
bool stateChanged() const;
bool isSameAsCurrentState(SerializedScriptValue*) const;
enum StateObjectType {
StateObjectPush,
StateObjectReplace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment