WebCore: https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injec...
authorhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2009 17:01:06 +0000 (17:01 +0000)
committerhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2009 17:01:06 +0000 (17:01 +0000)
Reviewed by Adam Roben.

This patch adds new API for adding and removing user scripts from page groups.  User scripts
are bundled together in isolated worlds (you can have multiple scripts together in the same
world).

Added userscripts/ directory for holding new tests (along with a simple test of script injection).

* WebCore.base.exp:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/ScriptController.cpp:
(WebCore::ScriptController::evaluateInIsolatedWorld):
* bindings/js/ScriptController.h:
* bindings/v8/ScriptController.cpp:
(WebCore::ScriptController::evaluateInIsolatedWorld):
* bindings/v8/ScriptController.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::finishedParsing):
(WebCore::FrameLoader::dispatchDocumentElementAvailable):
* page/Frame.cpp:
(WebCore::Frame::injectUserScripts):
(WebCore::Frame::injectUserScriptsForWorld):
* page/Frame.h:
* page/PageGroup.cpp:
(WebCore::PageGroup::~PageGroup):
(WebCore::PageGroup::addUserScript):
(WebCore::PageGroup::removeUserContentForWorld):
(WebCore::PageGroup::removeAllUserContent):
* page/PageGroup.h:
(WebCore::PageGroup::userScripts):
* page/UserScript.h: Added.
(WebCore::UserScript::UserScript):
(WebCore::UserScript::source):
(WebCore::UserScript::url):
(WebCore::UserScript::patterns):
(WebCore::UserScript::worldID):
(WebCore::UserScript::injectionTime):
* page/UserScriptTypes.h: Added.
(WebCore::):

WebKit/mac: https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injection work.

Reviewed by Adam Roben.

This patch adds new API for adding and removing user scripts from page groups.  User scripts
are bundled together in isolated worlds (you can have multiple scripts together in the same
world).

Added userscripts/ directory for holding new tests (along with a simple test of script injection).

* WebView/WebView.mm:
(+[WebView _addUserScriptToGroup:source:url:worldID:patterns:injectionTime:]):
(+[WebView _removeUserContentFromGroup:worldID:]):
(+[WebView _removeAllUserContentFromGroup:]):
* WebView/WebViewPrivate.h:

LayoutTests: https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injection work.

Reviewed by Adam Roben.

This patch adds new API for adding and removing user scripts from page groups.  User scripts
are bundled together in isolated worlds (you can have multiple scripts together in the same
world).

Added userscripts/ directory for holding new tests (along with a simple test of script injection).

* platform/mac/userscripts: Added.
* platform/mac/userscripts/script-run-at-end-expected.txt: Added.
* userscripts: Added.
* userscripts/script-run-at-end.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@48057 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/gtk/Skipped
LayoutTests/platform/mac/userscripts/script-run-at-end-expected.txt [new file with mode: 0644]
LayoutTests/platform/qt/Skipped
LayoutTests/platform/win/Skipped
LayoutTests/userscripts/script-run-at-end.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/GNUmakefile.am
WebCore/WebCore.base.exp
WebCore/WebCore.gypi
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bindings/js/ScriptController.cpp
WebCore/bindings/js/ScriptController.h
WebCore/bindings/v8/ScriptController.cpp
WebCore/bindings/v8/ScriptController.h
WebCore/loader/FrameLoader.cpp
WebCore/page/Frame.cpp
WebCore/page/Frame.h
WebCore/page/PageGroup.cpp
WebCore/page/PageGroup.h
WebCore/page/UserScript.h [new file with mode: 0644]
WebCore/page/UserScriptTypes.h [new file with mode: 0644]
WebKit/mac/ChangeLog
WebKit/mac/WebView/WebView.mm
WebKit/mac/WebView/WebViewPrivate.h
WebKitTools/DumpRenderTree/LayoutTestController.cpp
WebKitTools/DumpRenderTree/LayoutTestController.h
WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp
WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm
WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp
WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp

index c488de2..9e0073b 100644 (file)
@@ -1,3 +1,20 @@
+2009-09-03  Dave Hyatt  <hyatt@apple.com>
+
+        Reviewed by Adam Roben.
+
+        https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injection work.
+        
+        This patch adds new API for adding and removing user scripts from page groups.  User scripts
+        are bundled together in isolated worlds (you can have multiple scripts together in the same
+        world).
+
+        Added userscripts/ directory for holding new tests (along with a simple test of script injection).
+
+        * platform/mac/userscripts: Added.
+        * platform/mac/userscripts/script-run-at-end-expected.txt: Added.
+        * userscripts: Added.
+        * userscripts/script-run-at-end.html: Added.
+
 2009-09-04  Alexey Proskuryakov  <ap@apple.com>
 
         Reviewed by Eric Seidel.
index 847f410..eeef686 100644 (file)
@@ -5904,3 +5904,6 @@ fast/events/pageshow-pagehide.html
 # No TextInputController
 fast/forms/input-maxlength-ime-completed.html
 fast/forms/input-maxlength-ime-preedit.html
+
+# No User Scripts
+userscripts
diff --git a/LayoutTests/platform/mac/userscripts/script-run-at-end-expected.txt b/LayoutTests/platform/mac/userscripts/script-run-at-end-expected.txt
new file mode 100644 (file)
index 0000000..7ef22e9
--- /dev/null
@@ -0,0 +1 @@
+PASS
index 3fac0f7..8e4df71 100644 (file)
@@ -4831,3 +4831,6 @@ http/tests/xmlhttprequest/cross-origin-cookie-storage.html
 # https://bugs.webkit.org/show_bug.cgi?id=28952
 fast/workers/worker-multi-port.html
 fast/workers/worker-context-multi-port.html
+
+# No User Scripts
+userscripts
index 19624e5..6b66e51 100644 (file)
@@ -653,3 +653,6 @@ http/tests/xmlhttprequest/origin-whitelisting-subdomains.html
 # No TextInputController
 fast/forms/input-maxlength-ime-completed.html
 fast/forms/input-maxlength-ime-preedit.html
+
+# No User Scripts
+userscripts
diff --git a/LayoutTests/userscripts/script-run-at-end.html b/LayoutTests/userscripts/script-run-at-end.html
new file mode 100644 (file)
index 0000000..821b1c8
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.addUserScript("document.getElementById('target').innerHTML = 'PASS'", false);
+}
+</script>
+</head>
+<body>
+<div id="target">FAIL</div>
+</body>
+</html>
index 8bbbfdd..a6cf93f 100644 (file)
@@ -1,3 +1,48 @@
+2009-09-03  Dave Hyatt  <hyatt@apple.com>
+
+        Reviewed by Adam Roben.
+
+        https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injection work.
+        
+        This patch adds new API for adding and removing user scripts from page groups.  User scripts
+        are bundled together in isolated worlds (you can have multiple scripts together in the same
+        world).
+
+        Added userscripts/ directory for holding new tests (along with a simple test of script injection).
+
+        * WebCore.base.exp:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/ScriptController.cpp:
+        (WebCore::ScriptController::evaluateInIsolatedWorld):
+        * bindings/js/ScriptController.h:
+        * bindings/v8/ScriptController.cpp:
+        (WebCore::ScriptController::evaluateInIsolatedWorld):
+        * bindings/v8/ScriptController.h:
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::finishedParsing):
+        (WebCore::FrameLoader::dispatchDocumentElementAvailable):
+        * page/Frame.cpp:
+        (WebCore::Frame::injectUserScripts):
+        (WebCore::Frame::injectUserScriptsForWorld):
+        * page/Frame.h:
+        * page/PageGroup.cpp:
+        (WebCore::PageGroup::~PageGroup):
+        (WebCore::PageGroup::addUserScript):
+        (WebCore::PageGroup::removeUserContentForWorld):
+        (WebCore::PageGroup::removeAllUserContent):
+        * page/PageGroup.h:
+        (WebCore::PageGroup::userScripts):
+        * page/UserScript.h: Added.
+        (WebCore::UserScript::UserScript):
+        (WebCore::UserScript::source):
+        (WebCore::UserScript::url):
+        (WebCore::UserScript::patterns):
+        (WebCore::UserScript::worldID):
+        (WebCore::UserScript::injectionTime):
+        * page/UserScriptTypes.h: Added.
+        (WebCore::):
+
 2009-09-04  Alexey Proskuryakov  <ap@apple.com>
 
         Reviewed by Eric Seidel.
index 8bb8d3b..1753c3e 100644 (file)
@@ -1333,6 +1333,8 @@ webcore_sources += \
        WebCore/page/SecurityOriginHash.h \
        WebCore/page/Settings.cpp \
        WebCore/page/Settings.h \
+       WebCore/page/UserScript.h \
+       WebCore/page/UserScriptTypes.h \
        WebCore/page/WebKitPoint.h \
        WebCore/page/WindowFeatures.cpp \
        WebCore/page/WindowFeatures.h \
index 515d9bd..2439639 100644 (file)
@@ -742,10 +742,14 @@ __ZN7WebCore9HTMLNames9iframeTagE
 __ZN7WebCore9HTMLNames9scriptTagE
 __ZN7WebCore9PageCache11setCapacityEi
 __ZN7WebCore9PageCache27releaseAutoreleasedPagesNowEv
+__ZN7WebCore9PageGroup13addUserScriptERKNS_6StringERKNS_4KURLERKN3WTF6VectorIS1_Lm0EEEjNS_23UserScriptInjectionTimeE
 __ZN7WebCore9PageGroup14addVisitedLinkEPKtm
+__ZN7WebCore9PageGroup20removeAllUserContentEv
 __ZN7WebCore9PageGroup17closeLocalStorageEv
 __ZN7WebCore9PageGroup21removeAllVisitedLinksEv
+__ZN7WebCore9PageGroup25removeUserContentForWorldEj
 __ZN7WebCore9PageGroup26setShouldTrackVisitedLinksEb
+__ZN7WebCore9PageGroup9pageGroupERKNS_6StringE
 __ZN7WebCore9TimerBase4stopEv
 __ZN7WebCore9TimerBase5startEdd
 __ZN7WebCore9TimerBaseC2Ev
index acff621..580dbeb 100644 (file)
             'page/SecurityOriginHash.h',
             'page/Settings.cpp',
             'page/Settings.h',
+            'page/UserScript.h',
+            'page/UserScriptTypes.h',
             'page/WebKitPoint.h',
             'page/WindowFeatures.cpp',
             'page/WindowFeatures.h',
index 51a51b1..dd63175 100644 (file)
                                RelativePath="..\page\Settings.h"\r
                                >\r
                        </File>\r
+                        <File\r
+                               RelativePath="..\page\UserScript.h"\r
+                               >\r
+                       </File>\r
+                        <File\r
+                               RelativePath="..\page\UserScriptTypes.h"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath="..\page\WebKitPoint.h"\r
                                >\r
index 560f25d..bf5ae40 100644 (file)
                BC9BC64E0E7C4889008B9849 /* ScrollbarClient.h in Headers */ = {isa = PBXBuildFile; fileRef = BC9BC64D0E7C4889008B9849 /* ScrollbarClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                BCA169A20BFD55B40019CA76 /* JSHTMLTableCaptionElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCA169A00BFD55B40019CA76 /* JSHTMLTableCaptionElement.cpp */; };
                BCA169A30BFD55B40019CA76 /* JSHTMLTableCaptionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA169A10BFD55B40019CA76 /* JSHTMLTableCaptionElement.h */; };
+               BCA2B061105047600043BD1C /* UserScript.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA2B0601050475F0043BD1C /* UserScript.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               BCA2B08B10505BCD0043BD1C /* UserScriptTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA2B08A10505BCD0043BD1C /* UserScriptTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                BCA83E4F0D7CE1E9003421A8 /* JSClipboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCA83E4D0D7CE1E9003421A8 /* JSClipboard.cpp */; };
                BCA83E500D7CE1E9003421A8 /* JSClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA83E4E0D7CE1E9003421A8 /* JSClipboard.h */; };
                BCA83E520D7CE205003421A8 /* JSClipboardCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCA83E510D7CE205003421A8 /* JSClipboardCustom.cpp */; };
                BC9BC64D0E7C4889008B9849 /* ScrollbarClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollbarClient.h; sourceTree = "<group>"; };
                BCA169A00BFD55B40019CA76 /* JSHTMLTableCaptionElement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSHTMLTableCaptionElement.cpp; sourceTree = "<group>"; };
                BCA169A10BFD55B40019CA76 /* JSHTMLTableCaptionElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSHTMLTableCaptionElement.h; sourceTree = "<group>"; };
+               BCA2B0601050475F0043BD1C /* UserScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserScript.h; sourceTree = "<group>"; };
+               BCA2B08A10505BCD0043BD1C /* UserScriptTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserScriptTypes.h; sourceTree = "<group>"; };
                BCA378BA0D15F64200B793D6 /* ScheduledAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScheduledAction.cpp; sourceTree = "<group>"; };
                BCA378BB0D15F64200B793D6 /* ScheduledAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScheduledAction.h; sourceTree = "<group>"; };
                BCA83E360D7CDC4E003421A8 /* Clipboard.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Clipboard.idl; sourceTree = "<group>"; };
                                BCD0E0F90E972C3500265DEA /* SecurityOriginHash.h */,
                                14C9A5E90B3D105F005A0232 /* Settings.cpp */,
                                F587863A02DE3A1401EA4122 /* Settings.h */,
+                               BCA2B0601050475F0043BD1C /* UserScript.h */,
+                               BCA2B08A10505BCD0043BD1C /* UserScriptTypes.h */,
                                494BD7930F55C8EE00747828 /* WebKitPoint.h */,
                                494BD7940F55C8EE00747828 /* WebKitPoint.idl */,
                                BC8243E60D0CFD7500460C8F /* WindowFeatures.cpp */,
                                49C7B9E31042D32F0009D447 /* CanvasShader.h in Headers */,
                                49C7B9E61042D32F0009D447 /* CanvasTexture.h in Headers */,
                                49C7BA001042D38C0009D447 /* Canvas3DLayer.h in Headers */,
+                               BCA2B061105047600043BD1C /* UserScript.h in Headers */,
+                               BCA2B08B10505BCD0043BD1C /* UserScriptTypes.h in Headers */,
                                E1284BB110449FFA00EAEB52 /* JSPageTransitionEvent.h in Headers */,
                                E1284BD61044A01E00EAEB52 /* DOMPageTransitionEvent.h in Headers */,
                                510D4A34103165EE0049EA54 /* SocketStreamErrorBase.h in Headers */,
index f8134ca..4b33069 100644 (file)
@@ -131,6 +131,14 @@ ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
     return JSValue();
 }
 
+void ScriptController::evaluateInIsolatedWorld(unsigned /* worldID */, const Vector<ScriptSourceCode>& sourceCode) 
+{
+    // FIXME: Actually support isolated worlds!
+    unsigned size = sourceCode.size();
+    for (unsigned i = 0; i < size; ++i)
+        evaluate(sourceCode[i]);
+}
+
 void ScriptController::clearWindowShell()
 {
     if (!m_windowShell)
index 4528495..56e8f0c 100644 (file)
@@ -81,6 +81,7 @@ public:
     }
 
     ScriptValue evaluate(const ScriptSourceCode&);
+    void evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>&);
 
     void setEventHandlerLineNumber(int lineno) { m_handlerLineNumber = lineno; }
     int eventHandlerLineNumber() { return m_handlerLineNumber; }
index 1185f1a..c345e00 100644 (file)
@@ -179,6 +179,12 @@ bool ScriptController::processingUserGesture() const
     return false;
 }
 
+void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources)
+{
+    // FIXME: Get rid of extensionGroup here.
+    m_proxy->evaluateInNewWorld(sources, 1);
+}
+
 void ScriptController::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup)
 {
     m_proxy->evaluateInNewWorld(sources, extensionGroup);
index 8434c95..dc55efa 100644 (file)
@@ -64,10 +64,13 @@ namespace WebCore {
         // as a string.
         ScriptValue evaluate(const ScriptSourceCode&);
 
+        void evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>&);
+        
         // Executes JavaScript in a new world associated with the web frame. The
         // script gets its own global scope, its own prototypes for intrinsic
         // JavaScript objects (String, Array, and so-on), and its own wrappers for
         // all DOM nodes and DOM constructors.
+        // FIXME: Move to using evaluateInIsolatedWorld instead.
         void evaluateInNewWorld(const Vector<ScriptSourceCode>&, int extensionGroup);
 
         // Executes JavaScript in a new context associated with the web frame. The
index 8f0ddf0..9dbba31 100644 (file)
@@ -1213,6 +1213,8 @@ void FrameLoader::finishedParsing()
     if (m_creatingInitialEmptyDocument)
         return;
 
+    m_frame->injectUserScripts(InjectAtDocumentEnd);
+
     // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
     // because doing so will cause us to re-enter the destructor when protector goes out of scope.
     // Null-checking the FrameView indicates whether or not we're in the destructor.
@@ -5109,6 +5111,7 @@ String FrameLoader::referrer() const
 
 void FrameLoader::dispatchDocumentElementAvailable()
 {
+    m_frame->injectUserScripts(InjectAtDocumentStart);
     m_client->documentElementAvailable();
 }
 
index ff4c734..4ac2a3c 100644 (file)
@@ -60,6 +60,7 @@
 #include "Navigator.h"
 #include "NodeList.h"
 #include "Page.h"
+#include "PageGroup.h"
 #include "RegularExpression.h"
 #include "RenderPart.h"
 #include "RenderTableCell.h"
@@ -67,6 +68,8 @@
 #include "RenderTheme.h"
 #include "RenderView.h"
 #include "ScriptController.h"
+#include "ScriptSourceCode.h"
+#include "ScriptValue.h"
 #include "Settings.h"
 #include "TextIterator.h"
 #include "TextResourceDecoder.h"
@@ -841,6 +844,37 @@ void Frame::reapplyStyles()
     m_doc->updateStyleSelector();
 }
 
+void Frame::injectUserScripts(UserScriptInjectionTime injectionTime)
+{
+    ASSERT(m_page);
+    if (!m_page)
+        return;
+    
+    // Walk the hashtable. Inject by world.
+    const UserScriptMap* userScripts = m_page->group().userScripts();
+    if (!userScripts)
+        return;
+    UserScriptMap::const_iterator end = userScripts->end();
+    for (UserScriptMap::const_iterator it = userScripts->begin(); it != end; ++it)
+        injectUserScriptsForWorld(it->first, *it->second, injectionTime);
+}
+
+void Frame::injectUserScriptsForWorld(unsigned worldID, const UserScriptVector& userScripts, UserScriptInjectionTime injectionTime)
+{
+    if (userScripts.isEmpty())
+        return;
+
+    // FIXME: Need to implement pattern checking.
+    Vector<ScriptSourceCode> sourceCode;
+    unsigned count = userScripts.size();
+    for (unsigned i = 0; i < count; ++i) {
+        UserScript* script = userScripts[i].get();
+        if (script->injectionTime() == injectionTime)
+            sourceCode.append(ScriptSourceCode(script->source(), script->url()));
+    }
+    script()->evaluateInIsolatedWorld(worldID, sourceCode);
+}
+
 bool Frame::shouldChangeSelection(const VisibleSelection& newSelection) const
 {
     return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false);
index bdb42ec..00daee7 100644 (file)
@@ -41,6 +41,7 @@
 #include "ScrollBehavior.h"
 #include "SelectionController.h"
 #include "TextGranularity.h"
+#include "UserScriptTypes.h"
 
 #if PLATFORM(WIN)
 #include "FrameWin.h"
@@ -129,6 +130,10 @@ namespace WebCore {
         void createView(const IntSize&, const Color&, bool, const IntSize &, bool,
                         ScrollbarMode = ScrollbarAuto, ScrollbarMode = ScrollbarAuto);
 
+        void injectUserScripts(UserScriptInjectionTime);
+
+    private:
+        void injectUserScriptsForWorld(unsigned worldID, const UserScriptVector&, UserScriptInjectionTime);
 
     private:
         Frame(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*);
index 5155be1..90c44eb 100644 (file)
@@ -66,6 +66,11 @@ PageGroup::PageGroup(Page* page)
     addPage(page);
 }
 
+PageGroup::~PageGroup()
+{
+    removeAllUserContent();
+}
+
 typedef HashMap<String, PageGroup*> PageGroupMap;
 static PageGroupMap* pageGroups = 0;
 
@@ -193,4 +198,38 @@ StorageNamespace* PageGroup::localStorage()
 }
 #endif
 
+void PageGroup::addUserScript(const String& source, const KURL& url, const Vector<String>& patterns, 
+                              unsigned worldID, UserScriptInjectionTime injectionTime)
+{
+    if (worldID == UINT_MAX)
+        return;
+    OwnPtr<UserScript> userScript(new UserScript(source, url, patterns, worldID, injectionTime));
+    if (!m_userScripts)
+        m_userScripts.set(new UserScriptMap);
+    UserScriptVector*& scriptsInWorld = m_userScripts->add(worldID, 0).first->second;
+    if (!scriptsInWorld)
+        scriptsInWorld = new UserScriptVector;
+    scriptsInWorld->append(userScript.release());
+}
+
+void PageGroup::removeUserContentForWorld(unsigned worldID)
+{
+    if (!m_userScripts)
+        return;
+
+    UserScriptMap::iterator it = m_userScripts->find(worldID);
+    if (it != m_userScripts->end()) {
+        m_userScripts->remove(it);
+        delete it->second;
+    }
+}
+
+void PageGroup::removeAllUserContent()
+{
+    if (m_userScripts) {
+        deleteAllValues(*m_userScripts);
+        m_userScripts.clear();
+    }
+}
+
 } // namespace WebCore
index d04c2ae..af57c3d 100644 (file)
@@ -30,6 +30,7 @@
 #include <wtf/Noncopyable.h>
 #include "LinkHash.h"
 #include "StringHash.h"
+#include "UserScript.h"
 
 namespace WebCore {
 
@@ -41,6 +42,7 @@ namespace WebCore {
     public:
         PageGroup(const String& name);
         PageGroup(Page*);
+        ~PageGroup();
 
         static PageGroup* pageGroup(const String& groupName);
         static void closeLocalStorage();
@@ -67,6 +69,13 @@ namespace WebCore {
         bool hasLocalStorage() { return m_localStorage; }
 #endif
 
+        void addUserScript(const String& source, const KURL&, const Vector<String>& patterns,
+                           unsigned worldID, UserScriptInjectionTime);
+        const UserScriptMap* userScripts() const { return m_userScripts.get(); }
+
+        void removeUserContentForWorld(unsigned);
+        void removeAllUserContent();
+        
     private:
         void addVisitedLink(LinkHash stringHash);
 
@@ -81,6 +90,8 @@ namespace WebCore {
 #if ENABLE(DOM_STORAGE)
         RefPtr<StorageNamespace> m_localStorage;
 #endif
+
+        OwnPtr<UserScriptMap> m_userScripts;
     };
 
 } // namespace WebCore
diff --git a/WebCore/page/UserScript.h b/WebCore/page/UserScript.h
new file mode 100644 (file)
index 0000000..d7dda0a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * 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. 
+ */
+#ifndef UserScript_h
+#define UserScript_h
+
+#include "KURL.h"
+#include "UserScriptTypes.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class UserScript {
+public:
+    UserScript(const String& source, const KURL& url,
+               const Vector<String>& patterns, unsigned worldID,
+               UserScriptInjectionTime injectionTime)
+        : m_source(source)
+        , m_url(url)
+        , m_patterns(patterns)
+        , m_worldID(worldID)
+        , m_injectionTime(injectionTime)
+    {
+    }
+
+    const String& source() const { return m_source; }
+    const KURL& url() const { return m_url; }
+    const Vector<String>& patterns() const { return m_patterns; }
+    unsigned worldID() const { return m_worldID; }
+    UserScriptInjectionTime injectionTime() const { return m_injectionTime; }
+    
+private:
+    String m_source;
+    KURL m_url;
+    Vector<String> m_patterns;
+    unsigned m_worldID;
+    UserScriptInjectionTime m_injectionTime;
+};
+
+} // namsepace WebCore
+#endif // UserScript_h
diff --git a/WebCore/page/UserScriptTypes.h b/WebCore/page/UserScriptTypes.h
new file mode 100644 (file)
index 0000000..4111e56
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * 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. 
+ */
+#ifndef UserScriptTypes_h
+#define UserScriptTypes_h
+
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+enum UserScriptInjectionTime { InjectAtDocumentStart, InjectAtDocumentEnd };
+
+class UserScript;
+
+typedef Vector<OwnPtr<UserScript> > UserScriptVector;
+typedef HashMap<int, UserScriptVector*> UserScriptMap;
+
+} // namsepace WebCore
+#endif // UserScriptTypes_h
index 48f6a45..486f22b 100644 (file)
@@ -1,3 +1,21 @@
+2009-09-03  Dave Hyatt  <hyatt@apple.com>
+
+        Reviewed by Adam Roben.
+
+        https://bugs.webkit.org/show_bug.cgi?id=28890, make simple user script injection work.
+        
+        This patch adds new API for adding and removing user scripts from page groups.  User scripts
+        are bundled together in isolated worlds (you can have multiple scripts together in the same
+        world).
+
+        Added userscripts/ directory for holding new tests (along with a simple test of script injection).
+
+        * WebView/WebView.mm:
+        (+[WebView _addUserScriptToGroup:source:url:worldID:patterns:injectionTime:]):
+        (+[WebView _removeUserContentFromGroup:worldID:]):
+        (+[WebView _removeAllUserContentFromGroup:]):
+        * WebView/WebViewPrivate.h:
+
 2009-09-04  Adam Barth  <abarth@webkit.org>
 
         Reviewed by Eric Seidel.
index c064080..6531f26 100644 (file)
@@ -2110,6 +2110,54 @@ static inline IMP getMethod(id o, SEL s)
         _private->page->focusController()->setActive([[self window] isKeyWindow]);
 }
 
++ (void)_addUserScriptToGroup:(NSString *)groupName source:(NSString *)source url:(NSURL *)url worldID:(unsigned)worldID patterns:(NSArray *)patterns injectionTime:(WebUserScriptInjectionTime)injectionTime
+{
+    String group(groupName);
+    if (group.isEmpty() || worldID == UINT_MAX)
+        return;
+    
+    PageGroup* pageGroup = PageGroup::pageGroup(group);
+    if (!pageGroup)
+        return;
+    
+    // Convert the patterns into a Vector.
+    Vector<String> patternsVector;
+    NSUInteger count = [patterns count];
+    for (NSUInteger i = 0; i < count; ++i) {
+        id entry = [patterns objectAtIndex: i];
+        if ([entry isKindOfClass:[NSString class]])
+            patternsVector.append(String((NSString*)entry));
+    }
+    
+    pageGroup->addUserScript(source, url, patternsVector, worldID, 
+                             injectionTime == WebInjectAtDocumentStart ? InjectAtDocumentStart : InjectAtDocumentEnd);
+}
+
++ (void)_removeUserContentFromGroup:(NSString *)groupName worldID:(unsigned)worldID
+{
+    String group(groupName);
+    if (group.isEmpty())
+        return;
+    
+    PageGroup* pageGroup = PageGroup::pageGroup(group);
+    if (!pageGroup)
+        return;
+
+    pageGroup->removeUserContentForWorld(worldID);
+}
+
++ (void)_removeAllUserContentFromGroup:(NSString *)groupName
+{
+    String group(groupName);
+    if (group.isEmpty())
+        return;
+    
+    PageGroup* pageGroup = PageGroup::pageGroup(group);
+    if (!pageGroup)
+        return;
+    
+    pageGroup->removeAllUserContent();
+}
 @end
 
 @implementation _WebSafeForwarder
index 6c472d6..35dea78 100644 (file)
@@ -76,6 +76,11 @@ typedef enum {
 } WebDashboardBehavior;
 #endif
 
+typedef enum {
+    WebInjectAtDocumentStart,
+    WebInjectAtDocumentEnd,
+} WebUserScriptInjectionTime;
+
 @interface WebController : NSTreeController {
     IBOutlet WebView *webView;
 }
@@ -452,6 +457,10 @@ Could be worth adding to the API.
 // Removes all white list entries created with _whiteListAccessFromOrigin.
 + (void)_resetOriginAccessWhiteLists;
 
++ (void)_addUserScriptToGroup:(NSString *)groupName source:(NSString *)source url:(NSURL *)url worldID:(unsigned)worldID patterns:(NSArray *)patterns injectionTime:(WebUserScriptInjectionTime)injectionTime;
++ (void)_removeUserContentFromGroup:(NSString *)groupName worldID:(unsigned)worldID;
++ (void)_removeAllUserContentFromGroup:(NSString *)groupName;
+
 @end
 
 @interface WebView (WebViewPrintingPrivate)
index 34b4420..1aad52f 100644 (file)
@@ -912,6 +912,20 @@ static JSValueRef whiteListAccessFromOriginCallback(JSContextRef context, JSObje
     return JSValueMakeUndefined(context);
 }
 
+static JSValueRef addUserScriptCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    if (argumentCount != 2)
+        return JSValueMakeUndefined(context);
+    
+    JSRetainPtr<JSStringRef> source(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+    bool runAtStart = JSValueToBoolean(context, arguments[1]);
+    
+    LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+    controller->addUserScript(source.get(), runAtStart);
+    return JSValueMakeUndefined(context);
+}
 // Static Values
 
 static JSValueRef getGlobalFlagCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
@@ -988,6 +1002,7 @@ JSStaticFunction* LayoutTestController::staticFunctions()
 {
     static JSStaticFunction staticFunctions[] = {
         { "addDisallowedURL", addDisallowedURLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "addUserScript", addUserScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "clearAllDatabases", clearAllDatabasesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "clearBackForwardList", clearBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "clearPersistentUserStyleSheet", clearPersistentUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
index 88b3558..3147a96 100644 (file)
@@ -185,6 +185,8 @@ public:
 
     void whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains);
 
+    void addUserScript(JSStringRef source, bool runAtStart);
+
 private:
     bool m_dumpAsText;
     bool m_dumpAsPDF;
index 5dd975e..991da26 100644 (file)
@@ -448,3 +448,8 @@ void LayoutTestController::overridePreference(JSStringRef key, JSStringRef value
     g_free(name);
     g_free(strValue);
 }
+
+void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
+{
+    printf("LayoutTestController::addUserScript not implemented.\n");
+}
index a20ee4a..51ff549 100644 (file)
@@ -1078,6 +1078,7 @@ static void resetWebViewToConsistentStateBeforeTesting()
     [webView _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:NO];
     [webView _clearMainFrameName];
     [[webView undoManager] removeAllActions];
+    [WebView _removeAllUserContentFromGroup:[webView groupName]];
 
     resetDefaultsToConsistentValues();
 
index b04709b..2987d94 100644 (file)
@@ -464,3 +464,10 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J
     NSString *destinationHostNS = (NSString *)hostCF.get();
     [WebView _whiteListAccessFromOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
 }
+
+void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
+{
+    RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source));
+    NSString *sourceNS = (NSString *)sourceCF.get();
+    [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" source:sourceNS url:nil worldID:1 patterns:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)];
+}
index 5787977..7bc1d30 100644 (file)
@@ -831,3 +831,8 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J
 {
     printf("LayoutTestController::whiteListAccessFromOrigin not implemented\n");
 }
+
+void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
+{
+    printf("LayoutTestController::addUserScript not implemented.\n");
+}
index 21b0889..971367f 100644 (file)
@@ -277,3 +277,7 @@ void LayoutTestController::overridePreference(JSStringRef /* key */, JSStringRef
     // FIXME: implement
 }
 
+void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
+{
+    printf("LayoutTestController::addUserScript not implemented.\n");
+}