Commit c2e6fd7e authored by beidson@apple.com's avatar beidson@apple.com
Browse files

WebCore:

2008-04-07  Brady Eidson  <beidson@apple.com>

        Lovingly reviewed by Sam Weinig

        <rdar://problem/5797684> - HTML5 SessionStorage and underpinnings for LocalStorage

        Tests: storage/domstorage/sessionstorage/iframe-events.html
               storage/domstorage/sessionstorage/index-get-and-set.html
               storage/domstorage/sessionstorage/simple-events.html
               storage/domstorage/sessionstorage/simple-usage.html
               storage/domstorage/sessionstorage/window-open.html
               storage/domstorage/window-attributes-exist.html

        * Configurations/WebCore.xcconfig: Define to enable DOM_STORAGE

        * bindings/js/JSDOMWindowCustom.cpp:
        (WebCore::JSDOMWindow::mark): Add optionalSessionStorage case

        * bindings/js/JSEventCustom.cpp:
        (WebCore::toJS): Add StorageEvent case

        * bindings/js/JSStorageCustom.cpp:
        (WebCore::JSStorage::canGetItemsForName):
        (WebCore::JSStorage::nameGetter): If the property doesn't exist on the object, call through to getItem()
        (WebCore::JSStorage::customPut): If the property doesn't exist on the object, call through to setItem()

        * dom/Event.cpp:
        (WebCore::Event::isStorageEvent):
        * dom/Event.h:

        * dom/EventNames.h: Add "storage"

        * dom/EventTargetNode.cpp:
        (WebCore::EventTargetNode::dispatchStorageEvent):
        * dom/EventTargetNode.h:

        * loader/FrameLoader.cpp:
        (WebCore::FrameLoader::createWindow): After a new page has been created, set its SessionStorage object
          to a copy of the previous Page's

        * page/DOMWindow.cpp:
        (WebCore::DOMWindow::sessionStorage): Accessor to pull the appropriate OriginStorage out of the Page's
          SessionStorage.
        (WebCore::DOMWindow::localStorage): To be filled in later
        * page/DOMWindow.h:
        (WebCore::DOMWindow::optionalSessionStorage): Return the session Storage object for this window to mark, 
          if any exists
        * page/DOMWindow.idl:

        * page/Page.cpp:
        (WebCore::Page::sessionStorage):  Create and/or return the SessionStorage for this Page.
        (WebCore::Page::setSessionStorage): Set the SessionStorage for this Page - used in FrameLoader after a
          Window.open();
        * page/Page.h:

        * storage/OriginStorage.cpp: Intermediate layer between individual Storage objects, and shared StorageMap 
          objects.  There is one OriginStorage object per SecurityOrigin in each "unique set of storage areas", such
          as the SessionStorage.  This layer forwards DOM-level calls down to the backing StorageMap, handles 
          copy-on-write along with the StorageMap, fires StorageEvents to the DOM when a value is changed, and will
          eventually handle quota enforcement.
        (WebCore::OriginStorage::create):
        (WebCore::OriginStorage::OriginStorage):
        (WebCore::OriginStorage::~OriginStorage):
        (WebCore::OriginStorage::copy):
        (WebCore::OriginStorage::length):
        (WebCore::OriginStorage::key):
        (WebCore::OriginStorage::getItem):
        (WebCore::OriginStorage::setItem):
        (WebCore::OriginStorage::removeItem):
        (WebCore::OriginStorage::contains):
        (WebCore::OriginStorage::dispatchStorageEvent):
        * storage/OriginStorage.h:

        * storage/SessionStorage.cpp: From the HTML5 spec:
          "Each top-level browsing context has a unique set of session storage areas, one for each origin."
          This object represents that "unique set of session storage areas", and creates or returns the Storage
          object for the requested SecurityOrigin
        (WebCore::SessionStorage::create):
        (WebCore::SessionStorage::SessionStorage):
        (WebCore::SessionStorage::copy):
        (WebCore::SessionStorage::originStorage):
        * storage/SessionStorage.h:
        (WebCore::SessionStorage::page):

        * storage/Storage.cpp: Representation of the DOM-level object, wrapped by JSStorage.  There is a unique
          Storage object per Window (per-Frame) that wraps a specific shared OriginStorage object.
        (WebCore::Storage::create):
        (WebCore::Storage::Storage):
        (WebCore::Storage::length):
        (WebCore::Storage::key):
        (WebCore::Storage::getItem):
        (WebCore::Storage::setItem):
        (WebCore::Storage::removeItem):
        (WebCore::Storage::contains):
        * storage/Storage.h:
        * storage/Storage.idl:

        * storage/StorageEvent.cpp:
        (WebCore::StorageEvent::StorageEvent):
        (WebCore::StorageEvent::initStorageEvent):
        * storage/StorageEvent.h:
        (WebCore::StorageEvent::isStorageEvent):

        * storage/StorageMap.cpp: The physical map of key/value pairs that is shared between OriginStorage objects, 
          and implements copy-on-write semantics whenever a value is changed
        (WebCore::StorageMap::create):
        (WebCore::StorageMap::StorageMap):
        (WebCore::StorageMap::copy):
        (WebCore::StorageMap::invalidateIterator): Used to support the key(unsigned i) part of the API
        (WebCore::StorageMap::setIteratorToIndex): Ditto
        (WebCore::StorageMap::length):
        (WebCore::StorageMap::key):
        (WebCore::StorageMap::getItem):
        (WebCore::StorageMap::setItem):
        (WebCore::StorageMap::removeItem):
        (WebCore::StorageMap::contains):
        * storage/StorageMap.h:

LayoutTests:

2008-04-07  Brady Eidson  <beidson@apple.com>

        Begrudgingly reviewed by Sam Weinig

        Initial suite of layout tests for HTML5 key/value SessionStorage (<rdar://problem/5797684>)

        * fast/dom/Window/window-properties-expected.txt:
        * storage/domstorage: Added.
        * storage/domstorage/localstorage: Added.
        * storage/domstorage/sessionstorage: Added.
        * storage/domstorage/sessionstorage/iframe-events-expected.txt: Added.
        * storage/domstorage/sessionstorage/iframe-events.html: Added.
        * storage/domstorage/sessionstorage/index-get-and-set-expected.txt: Added.
        * storage/domstorage/sessionstorage/index-get-and-set.html: Added.
        * storage/domstorage/sessionstorage/resources: Added.
        * storage/domstorage/sessionstorage/resources/clearSessionStorage.js: Added.
        * storage/domstorage/sessionstorage/resources/iframe-events-second.html: Added.
        * storage/domstorage/sessionstorage/resources/window-open-second.html: Added.
        * storage/domstorage/sessionstorage/simple-events-expected.txt: Added.
        * storage/domstorage/sessionstorage/simple-events.html: Added.
        * storage/domstorage/sessionstorage/simple-usage-expected.txt: Added.
        * storage/domstorage/sessionstorage/simple-usage.html: Added.
        * storage/domstorage/sessionstorage/window-open-expected.txt: Added.
        * storage/domstorage/sessionstorage/window-open.html: Added.
        * storage/domstorage/window-attributes-exist-expected.txt: Added.
        * storage/domstorage/window-attributes-exist.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@31697 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent f02a7bed
2008-04-07 Brady Eidson <beidson@apple.com>
Begrudgingly reviewed by Sam Weinig
Initial suite of layout tests for HTML5 key/value SessionStorage (<rdar://problem/5797684>)
* fast/dom/Window/window-properties-expected.txt:
* storage/domstorage: Added.
* storage/domstorage/localstorage: Added.
* storage/domstorage/sessionstorage: Added.
* storage/domstorage/sessionstorage/iframe-events-expected.txt: Added.
* storage/domstorage/sessionstorage/iframe-events.html: Added.
* storage/domstorage/sessionstorage/index-get-and-set-expected.txt: Added.
* storage/domstorage/sessionstorage/index-get-and-set.html: Added.
* storage/domstorage/sessionstorage/resources: Added.
* storage/domstorage/sessionstorage/resources/clearSessionStorage.js: Added.
* storage/domstorage/sessionstorage/resources/iframe-events-second.html: Added.
* storage/domstorage/sessionstorage/resources/window-open-second.html: Added.
* storage/domstorage/sessionstorage/simple-events-expected.txt: Added.
* storage/domstorage/sessionstorage/simple-events.html: Added.
* storage/domstorage/sessionstorage/simple-usage-expected.txt: Added.
* storage/domstorage/sessionstorage/simple-usage.html: Added.
* storage/domstorage/sessionstorage/window-open-expected.txt: Added.
* storage/domstorage/sessionstorage/window-open.html: Added.
* storage/domstorage/window-attributes-exist-expected.txt: Added.
* storage/domstorage/window-attributes-exist.html: Added.
2008-04-07 Alexey Proskuryakov <ap@webkit.org>
 
Reviewed by Dan Bernstein.
......@@ -1261,6 +1261,7 @@ window.history.length [number]
window.innerHeight [number]
window.innerWidth [number]
window.length [number]
window.localStorage [null]
window.location [object Location]
window.location.assign [function]
window.location.hash [string]
......@@ -1347,6 +1348,12 @@ window.scrollY [number]
window.scrollbars [object BarInfo]
window.scrollbars.visible [boolean]
window.self [printed above as window]
window.sessionStorage [object Storage]
window.sessionStorage.getItem [function]
window.sessionStorage.key [function]
window.sessionStorage.length [number]
window.sessionStorage.removeItem [function]
window.sessionStorage.setItem [function]
window.setInterval [function]
window.setTimeout [function]
window.status [string]
......
This is the main frame of a 2-frame document. Each frame is in the same security origin and therefore shares the same sessionStorage object. As a result, each frame should receive a StorageEvent when either frame changes the sessionStorage object.
Main frame about to change sessionStorage...
Main Frame received StorageEvent:
Key - Main Frame
New Value - SET
Old Value - null
Subframe received storage event:
Key - Main Frame
New Value - SET
Old Value - null
Subframe about to change sessionStorage...
Main Frame received StorageEvent:
Key - Subframe
New Value - SET
Old Value - null
Subframe received storage event:
Key - Subframe
New Value - SET
Old Value - null
<html>
<head>
<script src="resources/clearSessionStorage.js"></script>
<script>
if (layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function log(a)
{
document.getElementById("logger").innerHTML += a + "<br>";
}
function finish()
{
if (layoutTestController)
layoutTestController.notifyDone()
}
function handleStorageEvent(e)
{
log("Main Frame received StorageEvent:");
log("Key - " + e.key);
log("New Value - " + e.newValue);
log("Old Value - " + e.oldValue);
log("");
if (e.key == "Subframe")
finish();
}
function startTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
finish();
return;
}
document.body.addEventListener("storage", handleStorageEvent, false);
log("Main frame about to change sessionStorage...");
sessionStorage.setItem("Main Frame", "SET");
}
</script>
</head>
<body onload="startTest();">
This is the main frame of a 2-frame document. Each frame is in the same security origin and therefore shares the same sessionStorage object.
As a result, each frame should receive a StorageEvent when either frame changes the sessionStorage object.<br>
<iframe src="resources/iframe-events-second.html"></iframe><br>
<div id="logger"></div>
</body>
</html>
This is a test to make sure you can get and set values in SessionStorage by index.
Setting FOO using the index setter.
Storage event fired:
Key - FOO
New Value - BAR
Old Value - null
Reading FOO:
BAR
BAR
BAR
Setting FOO again, using setItem.
Storage event fired:
Key - FOO
New Value - BAZ
Old Value - BAR
Reading FOO:
BAZ
BAZ
BAZ
Setting FOO again, using the index setter.
Storage event fired:
Key - FOO
New Value - BAT
Old Value - BAZ
Reading FOO:
BAT
BAT
BAT
Setting FOO again, using property-slot syntax
Storage event fired:
Key - FOO
New Value - BATMAN
Old Value - BAT
Reading FOO:
BATMAN
BATMAN
BATMAN
Removing FOO, then trying to read it
Storage event fired:
Key - FOO
New Value - null
Old Value - BATMAN
Reading FOO:
undefined
undefined
null
<html>
<head>
<script src="resources/clearSessionStorage.js"></script>
<script>
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function log(a)
{
document.getElementById("logger").innerHTML += a + "<br>";
}
function finish()
{
if (layoutTestController)
layoutTestController.notifyDone()
}
function handleStorageEvent(e)
{
log("Storage event fired:");
log("Key - " + e.key);
log("New Value - " + e.newValue);
log("Old Value - " + e.oldValue);
log("");
}
function runTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
finish();
return;
}
document.body.addEventListener("storage", handleStorageEvent, false);
log("Setting FOO using the index setter.");
sessionStorage["FOO"] = "BAR";
log("Reading FOO:");
log(sessionStorage.FOO);
log(sessionStorage["FOO"]);
log(sessionStorage.getItem("FOO"));
log("");
log("Setting FOO again, using setItem.");
sessionStorage.setItem("FOO", "BAZ");
log("Reading FOO:");
log(sessionStorage.FOO);
log(sessionStorage["FOO"]);
log(sessionStorage.getItem("FOO"));
log("");
log("Setting FOO again, using the index setter.");
sessionStorage["FOO"] = "BAT";
log("Reading FOO:");
log(sessionStorage.FOO);
log(sessionStorage["FOO"]);
log(sessionStorage.getItem("FOO"));
log("");
log("Setting FOO again, using property-slot syntax");
sessionStorage.FOO = "BATMAN";
log("Reading FOO:");
log(sessionStorage.FOO);
log(sessionStorage["FOO"]);
log(sessionStorage.getItem("FOO"));
log("");
log("Removing FOO, then trying to read it");
sessionStorage.removeItem("FOO");
log("Reading FOO:");
log(sessionStorage.FOO);
log(sessionStorage["FOO"]);
log(sessionStorage.getItem("FOO"));
log("");
finish();
}
</script>
</head>
<body onload="runTest();">
This is a test to make sure you can get and set values in SessionStorage by index.<br>
<div id="logger"></div>
</body>
</html>
function clearSessionStorage()
{
var i = 0;
for (;i < sessionStorage.length; ++i)
sessionStorage.removeItem(sessionStorage.key(i));
}
if (window.sessionStorage)
clearSessionStorage();
<html>
<head>
<script>
function log(a)
{
parent.document.getElementById("logger").innerHTML += a + "<br>";
}
function handleStorageEvent(e)
{
log("Subframe received storage event:");
log("Key - " + e.key);
log("New Value - " + e.newValue);
log("Old Value - " + e.oldValue);
log("");
if (e.key != "Subframe") {
log("Subframe about to change sessionStorage...");
sessionStorage.setItem("Subframe", "SET");
}
}
</script>
</head>
<body onload="document.body.addEventListener('storage', handleStorageEvent, false);">
This is the subframe which exists to make sure that both frames of a same security origin receive the event for that origin's sessionStorage object mutating
</body>
</html>
<html>
<head>
<script>
var secondWindowLog = "Logging from second window:<br>";
function log(a)
{
secondWindowLog += a + "<br>";
}
function runTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
return;
}
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
window.sessionStorage.setItem("FOO", "BAR-NEWWINDOW");
log("Value for FOO after changing my own copy is " + window.sessionStorage.getItem("FOO"));
log("Value for FOO in my opening window is " + window.opener.sessionStorage.getItem("FOO"));
window.opener.log(secondWindowLog);
if (layoutTestController)
layoutTestController.notifyDone();
}
</script>
</head>
<body onload="runTest();">
This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change<br>
</body>
</html>
This is a test to make sure SessionStorage mutations fire StorageEvents
Storage event fired:
Key - FOO
New Value - BAR
Old Value - null
Event has a URI
Event has a source DOMWindow
Storage event fired:
Key - FU
New Value - BAR
Old Value - null
Event has a URI
Event has a source DOMWindow
Storage event fired:
Key - FOO
New Value - null
Old Value - BAR
Event has a URI
Event has a source DOMWindow
Storage event fired:
Key - FU
New Value - null
Old Value - BAR
Event has a URI
Event has a source DOMWindow
<html>
<head>
<script src="resources/clearSessionStorage.js"></script>
<script>
if (layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
function log(a)
{
document.getElementById("logger").innerHTML += a + "<br>";
}
function finish()
{
if (layoutTestController)
layoutTestController.notifyDone()
}
function handleStorageEvent(e)
{
log("Storage event fired:");
log("Key - " + e.key);
log("New Value - " + e.newValue);
log("Old Value - " + e.oldValue);
if (e.uri)
log("Event has a URI");
if (e.source)
log("Event has a source DOMWindow");
log("");
}
function runTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
finish();
return;
}
document.body.addEventListener("storage", handleStorageEvent, false);
window.sessionStorage.setItem("FOO", "BAR");
window.sessionStorage.setItem("FU", "BAR");
window.sessionStorage.removeItem("FOO");
window.sessionStorage.removeItem("FU");
finish();
}
</script>
</head>
<body onload="runTest();">
This is a test to make sure SessionStorage mutations fire StorageEvents<br>
<div id="logger"></div>
</body>
</html>
This test trys simple operations on SessionStorage
Length is 0
Value for FOO is null
Length is 1
Value for FOO is BAR
Key for index 0 is FOO
Correctly caught exception trying to access key at index 1
Error: INDEX_SIZE_ERR: DOM Exception 1
Length is 1
Value for FOO is BAZ
Length is 0
Value for FOO is null
<html>
<head>
<script src="resources/clearSessionStorage.js"></script>
<script>
if (layoutTestController)
layoutTestController.dumpAsText();
function log(a)
{
document.getElementById("logger").innerHTML += a + "<br>";
}
function runTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
return;
}
log("Length is " + window.sessionStorage.length);
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
window.sessionStorage.setItem("FOO", "BAR");
log("Length is " + window.sessionStorage.length);
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
log("Key for index 0 is " + window.sessionStorage.key(0));
try {
window.sessionStorage.key(1);
} catch(e) {
log("Correctly caught exception trying to access key at index 1");
log(e);
}
window.sessionStorage.setItem("FOO", "BAZ");
log("Length is " + window.sessionStorage.length);
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
window.sessionStorage.removeItem("FOO");
log("Length is " + window.sessionStorage.length);
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
}
</script>
</head>
<body onload="runTest();">
This test trys simple operations on SessionStorage<br>
<div id="logger"></div>
</body>
</html>
This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change
Value for FOO is BAR
Logging from second window:
Value for FOO is BAR
Value for FOO after changing my own copy is BAR-NEWWINDOW
Value for FOO in my opening window is BAR
<html>
<head>
<script src="resources/clearSessionStorage.js"></script>
<script>
if (layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.setCanOpenWindows();
layoutTestController.waitUntilDone();
}
function log(a)
{
document.getElementById("logger").innerHTML += a + "<br>";
}
function runTest()
{
if (!window.sessionStorage) {
log("window.sessionStorage DOES NOT exist");
return;
}
window.log = log;
window.sessionStorage.setItem("FOO", "BAR");
log("Value for FOO is " + window.sessionStorage.getItem("FOO"));
window.open("resources/window-open-second.html");
}
</script>
</head>
<body onload="runTest();">
This is a new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change<br>
<div id="logger"></div>
</body>
</html>
This test checks to see if window.localStorage and window.sessionStorage exist.
window.sessionStorage exists
Storage object sessionStorage has length
Storage object sessionStorage has key
Storage object sessionStorage has getItem
Storage object sessionStorage has setItem
Storage object sessionStorage has removeItem
window.localStorage DOES NOT exist