Reviewed by Darin Adler.
Fix for http://bugs.webkit.org/show_bug.cgi?id=14959
No back forward entry added for pages created in javascript
A new HistoryItem is created for calls to Document::open. Calls to
Document::write save the written data to a SharedBuffer that is also
stored on the HistoryItem. When the user navigates back to a
HistoryItem that has a valid buffer, that data is used for the page
content.
Tests: http/tests/navigation/document-open-adds-history-item.html
http/tests/navigation/document-open-delayed-adds-history-item.html
http/tests/navigation/document-open-new-window-adds-history-item.html
http/tests/navigation/document-open-replace-no-history-item.html
* bindings/js/JSHTMLDocumentCustom.cpp:
(WebCore::JSHTMLDocument::open):
* dom/Document.cpp:
(WebCore::Document::open):
(WebCore::Document::write):
(WebCore::Document::clear):
* dom/Document.h:
* history/HistoryItem.cpp:
(WebCore::HistoryItem::HistoryItem):
(WebCore::HistoryItem::substituteData):
(WebCore::HistoryItem::setSubstituteData):
* history/HistoryItem.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::didExplicitOpen):
(WebCore::FrameLoader::load):
(WebCore::FrameLoader::reloadAllowingStaleData):
(WebCore::FrameLoader::reload):
(WebCore::FrameLoader::shouldTreatURLAsSameAsCurrent):
(WebCore::FrameLoader::loadItem):
* loader/FrameLoader.h:
2008-02-04 Matt Perry <mpComplete@gmail.com>
Reviewed by Darin Adler.
Test cases for fix to http://bugs.webkit.org/show_bug.cgi?id=14959
No back forward entry added for pages created in javascript.
* http/tests/navigation/document-open-adds-history-item-expected.txt: Added.
* http/tests/navigation/document-open-adds-history-item.html: Added.
* http/tests/navigation/document-open-delayed-adds-history-item-expected.txt: Added.
* http/tests/navigation/document-open-delayed-adds-history-item.html: Added.
* http/tests/navigation/document-open-new-window-adds-history-item-expected.txt: Added.
* http/tests/navigation/document-open-new-window-adds-history-item.html: Added.
* http/tests/navigation/document-open-replace-no-history-item-expected.txt: Added.
* http/tests/navigation/document-open-replace-no-history-item.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29998
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2008-02-04 Matt Perry <mpComplete@gmail.com>
+
+ Reviewed by Darin Adler.
+
+ Test cases for fix to http://bugs.webkit.org/show_bug.cgi?id=14959
+ No back forward entry added for pages created in javascript.
+
+ * http/tests/navigation/document-open-adds-history-item-expected.txt: Added.
+ * http/tests/navigation/document-open-adds-history-item.html: Added.
+ * http/tests/navigation/document-open-delayed-adds-history-item-expected.txt: Added.
+ * http/tests/navigation/document-open-delayed-adds-history-item.html: Added.
+ * http/tests/navigation/document-open-new-window-adds-history-item-expected.txt: Added.
+ * http/tests/navigation/document-open-new-window-adds-history-item.html: Added.
+ * http/tests/navigation/document-open-replace-no-history-item-expected.txt: Added.
+ * http/tests/navigation/document-open-replace-no-history-item.html: Added.
+
2008-02-04 Cameron Zwarich <cwzwarich@uwaterloo.ca>
Reviewed by Oliver Hunt.
--- /dev/null
+This generated document and its contents should be in the back/forward list.
+
+============== Back Forward List ==============
+ http://127.0.0.1:8000/navigation/document-open-adds-history-item.html **nav target**
+curr-> http://127.0.0.1:8000/navigation/document-open-adds-history-item.html **nav target**
+ http://127.0.0.1:8000/navigation/resources/document-open-page-2.html **nav target**
+===============================================
--- /dev/null
+<html>
+<head>
+<script src="resources/document-open.js"></script>
+<script>
+ function runTest()
+ {
+ window.firstVisit = true;
+ document.open();
+ document.writeln("<script>" + window.stopTest + "<" + "/script>");
+ document.writeln("<body onload='stopTest();'>");
+ document.writeln("This generated document and its contents should be in the back/forward list.");
+ document.writeln("</body>");
+ document.close();
+ }
+</script>
+</head>
+<body onload="startTest()">
+This tests that document.open creates a back/forward item.
+</body>
+</html>
--- /dev/null
+This generated document and its contents should be in the back/forward list... even with a delayed close.
+
+============== Back Forward List ==============
+ http://127.0.0.1:8000/navigation/document-open-delayed-adds-history-item.html **nav target**
+curr-> http://127.0.0.1:8000/navigation/document-open-delayed-adds-history-item.html **nav target**
+ http://127.0.0.1:8000/navigation/resources/document-open-page-2.html **nav target**
+===============================================
--- /dev/null
+<html>
+<head>
+<script src="resources/document-open.js"></script>
+<script>
+ function runTest()
+ {
+ window.firstVisit = true;
+ document.open();
+ document.writeln("<script>" + window.stopTest + "<" + "/script>");
+ document.writeln("<body onload='stopTest();'>");
+ document.writeln("This generated document and its contents should be in the back/forward list...");
+ setTimeout("runTest2();", 0);
+ }
+ function runTest2()
+ {
+ document.writeln("even with a delayed close.");
+ document.writeln("</body>");
+ document.close();
+ }
+</script>
+</head>
+<body onload="startTest()">
+This tests that document.open creates a back/forward item, and the page loads
+correctly even when writes are split across a timeout.
+</body>
+</html>
--- /dev/null
+This tests that document.open in a new window creates a back/forward item.
+
+============== Back Forward List ==============
+curr-> http://127.0.0.1:8000/navigation/document-open-new-window-adds-history-item.html **nav target**
+===============================================
+
+============== Back Forward List ==============
+curr-> about:blank **nav target**
+ http://127.0.0.1:8000/navigation/resources/document-open-page-2.html **nav target**
+===============================================
--- /dev/null
+<html>
+<head>
+<script src="resources/document-open.js"></script>
+<script>
+ function runTest()
+ {
+ if (window.layoutTestController) {
+ layoutTestController.setCanOpenWindows();
+ }
+ w = window.open('');
+ w.firstVisit = true;
+ w.focus();
+ w.document.open();
+ w.document.writeln("<script>" + window.stopTest + "<" + "/script>");
+ w.document.writeln("<body onload='stopTest();'>");
+ w.document.writeln("This generated document and its contents should be in the back/forward list.");
+ w.document.writeln("</body>");
+ w.document.close();
+ }
+</script>
+</head>
+<body onload="startTest()">
+This tests that document.open in a new window creates a back/forward item.
+</body>
+</html>
--- /dev/null
+This generated document and its contents should be in the back/forward list. This page should have replaced the originating page as well.
+
+============== Back Forward List ==============
+curr-> http://127.0.0.1:8000/navigation/document-open-replace-no-history-item.html **nav target**
+ http://127.0.0.1:8000/navigation/resources/document-open-page-2.html **nav target**
+===============================================
--- /dev/null
+<html>
+<head>
+<script src="resources/document-open.js"></script>
+<script>
+ function runTest()
+ {
+ window.firstVisit = true;
+ document.open("text/html", "replace");
+ document.writeln("<script>" + window.stopTest + "<" + "/script>");
+ document.writeln("<body onload='stopTest();'>");
+ document.writeln("This generated document and its contents should be in the back/forward list.");
+ document.writeln("This page should have replaced the originating page as well.");
+ document.writeln("</body>");
+ document.close();
+ }
+</script>
+</head>
+<body onload="startTest()">
+This tests that document.open does not create a back/forward item if "replace"
+is specified.
+</body>
+</html>
--- /dev/null
+<head>
+<script>
+ function goBack()
+ {
+ window.history.back();
+ }
+</script>
+</head>
+<body onload="goBack()">
+Just navigates back to where it came from.
+</body>
--- /dev/null
+// Common testing functions for document-open history tests.
+
+function startTest()
+{
+ if (window.layoutTestController) {
+ layoutTestController.dumpBackForwardList();
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ setTimeout("runTest()", 0);
+ }
+}
+
+function stopTest()
+{
+ // We expect the test to set window.firstVisit to true before it open
+ // the document. When we navigate away, this variable will be cleared.
+ if (window.firstVisit) {
+ // This is the first time we loaded this page.
+ // Navigate away and back to ensure the generated contents remain intact.
+ window.location = "resources/document-open-page-2.html";
+ window.firstVisit = false; // just to be explicit.
+ } else {
+ // We are now returning from page-2. End the test.
+ if (window.layoutTestController) {
+ layoutTestController.notifyDone();
+ }
+ }
+}
+2008-02-04 Matt Perry <mpComplete@gmail.com>
+
+ Reviewed by Darin Adler.
+
+ Fix for http://bugs.webkit.org/show_bug.cgi?id=14959
+ No back forward entry added for pages created in javascript
+
+ A new HistoryItem is created for calls to Document::open. Calls to
+ Document::write save the written data to a SharedBuffer that is also
+ stored on the HistoryItem. When the user navigates back to a
+ HistoryItem that has a valid buffer, that data is used for the page
+ content.
+
+ Tests: http/tests/navigation/document-open-adds-history-item.html
+ http/tests/navigation/document-open-delayed-adds-history-item.html
+ http/tests/navigation/document-open-new-window-adds-history-item.html
+ http/tests/navigation/document-open-replace-no-history-item.html
+
+ * bindings/js/JSHTMLDocumentCustom.cpp:
+ (WebCore::JSHTMLDocument::open):
+ * dom/Document.cpp:
+ (WebCore::Document::open):
+ (WebCore::Document::write):
+ (WebCore::Document::clear):
+ * dom/Document.h:
+ * history/HistoryItem.cpp:
+ (WebCore::HistoryItem::HistoryItem):
+ (WebCore::HistoryItem::substituteData):
+ (WebCore::HistoryItem::setSubstituteData):
+ * history/HistoryItem.h:
+ * loader/FrameLoader.cpp:
+ (WebCore::FrameLoader::didExplicitOpen):
+ (WebCore::FrameLoader::load):
+ (WebCore::FrameLoader::reloadAllowingStaleData):
+ (WebCore::FrameLoader::reload):
+ (WebCore::FrameLoader::shouldTreatURLAsSameAsCurrent):
+ (WebCore::FrameLoader::loadItem):
+ * loader/FrameLoader.h:
+
2008-02-04 Mark Rowe <mrowe@apple.com>
Unreviewed Gtk build fix.
}
// In the case of two parameters or fewer, do a normal document open.
- static_cast<HTMLDocument*>(impl())->open();
+
+ String mimeType;
+ if (!args[0]->isUndefined()) {
+ mimeType = String(args[0]->toString(exec)).lower();
+ // Anything other than text/html is treated as plaintext.
+ if (mimeType != "text/html")
+ mimeType = "text/plain";
+ } else
+ mimeType = "text/html";
+
+ bool replace = equalIgnoringCase("replace", String(args[1]->toString(exec)));
+
+ static_cast<HTMLDocument*>(impl())->open(mimeType, replace);
return jsUndefined();
}
#include "AXObjectCache.h"
#include "CDATASection.h"
+#include "CString.h"
#include "CSSHelper.h"
#include "CSSStyleSelector.h"
#include "CSSStyleSheet.h"
#include "FrameLoader.h"
#include "FrameTree.h"
#include "FrameView.h"
+#include "HistoryItem.h"
#include "HTMLBodyElement.h"
#include "HTMLDocument.h"
#include "HTMLElementFactory.h"
void Document::open()
{
+ // This method is called by various places in the WebCore code. Arguments
+ // chosen for legacy reasons.
+ open("text/html", true);
+}
+
+void Document::open(const String& mimeType, bool replace)
+{
+ // Calling open() during an onload handler is like a redirect, so we should not add a new
+ // history item.
+ if (m_processingLoadEvent)
+ replace = true;
+
// This is work that we should probably do in clear(), but we can't have it
// happen when implicitOpen() is called unless we reorganize Frame code.
if (Document *parent = parentDocument()) {
if (m_frame->loader()->state() == FrameStateProvisional)
m_frame->loader()->stopAllLoaders();
}
-
+
implicitOpen();
- if (m_frame)
- m_frame->loader()->didExplicitOpen();
+ if (m_frame) {
+ m_textWrittenByScript = new SharedBuffer;
+ m_frame->loader()->didExplicitOpen(mimeType, replace, m_textWrittenByScript.get());
+ }
}
void Document::cancelParsing()
if (!ownerElement())
printf("Beginning a document.write at %d\n", elapsedTime());
#endif
-
+
if (!m_tokenizer) {
- open();
+ open("text/html", false);
ASSERT(m_tokenizer);
if (!m_tokenizer)
return;
write("<html>");
}
m_tokenizer->write(text, false);
-
+
+ if (m_textWrittenByScript)
+ m_textWrittenByScript->append(reinterpret_cast<const char*>(text.characters()),
+ text.length() * sizeof(UChar));
+
#ifdef INSTRUMENT_LAYOUT_SCHEDULING
if (!ownerElement())
printf("Ending a document.write at %d\n", elapsedTime());
-#endif
+#endif
}
void Document::writeln(const String& text)
delete m_tokenizer;
m_tokenizer = 0;
+ m_textWrittenByScript = 0;
+
removeChildren();
m_windowEventListeners.clear();
#include "DocumentMarker.h"
#include "HTMLCollection.h"
#include "HTMLFormElement.h"
+#include "SharedBuffer.h"
#include "StringHash.h"
#include "Timer.h"
#include <wtf/HashCountedSet.h>
void setVisuallyOrdered();
void open();
+ void open(const String& mimeType, bool replace);
void implicitOpen();
void close();
void implicitClose();
bool m_isXHTML;
+ // Contains the text written to the document by script, eg through document.write().
+ RefPtr<SharedBuffer> m_textWrittenByScript;
+
unsigned m_numNodeLists;
#if ENABLE(DATABASE)
, m_formContentType(item.m_formContentType)
, m_formReferrer(item.m_formReferrer)
, m_rssFeedReferrer(item.m_rssFeedReferrer)
+ , m_substituteData(item.m_substituteData)
{
if (item.m_formData)
m_formData = item.m_formData->copy();
m_rssFeedReferrer = referrer;
}
+const SubstituteData& HistoryItem::substituteData() const
+{
+ return m_substituteData;
+}
+
+void HistoryItem::setSubstituteData(const SubstituteData& substituteData)
+{
+ m_substituteData = substituteData;
+}
+
void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
{
if (equalIgnoringCase(request.httpMethod(), "POST")) {
#include "PlatformString.h"
#include <wtf/RefCounted.h>
#include "StringHash.h"
+#include "SubstituteData.h"
#include <wtf/HashMap.h>
#include <wtf/OwnPtr.h>
#include <wtf/RefPtr.h>
String formContentType() const;
String formReferrer() const;
String rssFeedReferrer() const;
+ const SubstituteData& substituteData() const;
int visitCount() const;
void setRSSFeedReferrer(const String&);
void setVisitCount(int);
+ void setSubstituteData(const SubstituteData&);
void addChildItem(PassRefPtr<HistoryItem>);
HistoryItem* childItemWithName(const String&) const;
// info used to support RSS feeds
String m_rssFeedReferrer;
+ SubstituteData m_substituteData;
+
// PageCache controls these fields.
HistoryItem* m_next;
HistoryItem* m_prev;
return true;
}
-void FrameLoader::didExplicitOpen()
+void FrameLoader::didExplicitOpen(const String& mimeType, bool replace, SharedBuffer* buffer)
{
m_isComplete = false;
m_didCallImplicitClose = false;
cancelRedirection();
if (m_frame->document()->url() != "about:blank")
m_URL = m_frame->document()->url();
+
+ bool isItemNew = false;
+
+ // Add a HistoryItem for this open.
+ RefPtr<HistoryItem> item;
+ if (replace && m_currentHistoryItem)
+ item = m_currentHistoryItem;
+ else {
+ isItemNew = true;
+ item = new HistoryItem(m_URL, m_frame->tree()->name(), m_frame->tree()->parent() ? m_frame->tree()->parent()->tree()->name() : "", "");
+ item->setIsTargetItem(true);
+ m_previousHistoryItem = m_currentHistoryItem;
+ m_currentHistoryItem = item;
+ }
+
+ // Create an alternate URL to distinguish this as a generated page.
+ KURL generatedURL("webkitgenerated:" + m_frame->document()->url());
+
+ item->setSubstituteData(SubstituteData(buffer, mimeType, "UTF-16", m_URL, generatedURL));
+
+ if (isItemNew)
+ if (Page* page = m_frame->page())
+ page->backForwardList()->addItem(item);
}
bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState)
{
- RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+ load(request, action, type, formState, SubstituteData());
+}
+
+void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState, const SubstituteData& substituteData)
+{
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData);
loader->setTriggeringAction(action);
if (m_documentLoader)
request.setCachePolicy(ReturnCacheDataElseLoad);
- RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, m_currentHistoryItem->substituteData());
+ setProvisionalHistoryItem(m_currentHistoryItem);
+
setPolicyDocumentLoader(loader.get());
loader->setOverrideEncoding(encoding);
if (!unreachableURL.isEmpty())
initialRequest = ResourceRequest(unreachableURL);
- RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
+ RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, m_currentHistoryItem->substituteData());
+ setProvisionalHistoryItem(m_currentHistoryItem);
ResourceRequest& request = loader->request();
{
if (!m_currentHistoryItem)
return false;
+ if (m_currentHistoryItem->substituteData().isValid())
+ return url == m_currentHistoryItem->substituteData().responseURL();
return url == m_currentHistoryItem->url() || url == m_currentHistoryItem->originalURL();
}
action = NavigationAction(itemOriginalURL, loadType, false);
}
- load(request, action, loadType, 0);
+ load(request, action, loadType, 0, item->substituteData());
}
}
}
void load(const ResourceRequest&, const SubstituteData&);
void load(const ResourceRequest&, const String& frameName);
void load(const ResourceRequest&, const NavigationAction&, FrameLoadType, PassRefPtr<FormState>);
+ void load(const ResourceRequest&, const NavigationAction&, FrameLoadType, PassRefPtr<FormState>, const SubstituteData&);
void load(DocumentLoader*);
void load(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>);
void stopLoading(bool sendUnload);
bool closeURL();
- void didExplicitOpen();
+ void didExplicitOpen(const String& mimeType, bool replace, SharedBuffer*);
KURL iconURL();
void commitIconURLToIconDatabase(const KURL&);