はじめに
サムネイルサーバーのようなサービスを作るときには、ウェブサーバー上にブラウザを乗せる必要があります。
問題
ただ、そういった用途でブラウザを使う場合。
JavaScript が無限ループに落ち入らないように配慮する必要があります。
ほとんどの場合は、 JavaScript 自体をオフにすることが多いのですが JavaScript を実行したいような場合もあるでしょう。
解決方法
今回は、 WebKit でその解決方法を紹介します。
2. 次に、タイマーの時間を短くする
WebCore/bindings/js/JSDOMWindowBase.cpp を編集
JSDOMWindowBase::JSDOMWindowBase(PassRefPtr<StructureID> structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) : JSGlobalObject(structure, new JSDOMWindowBaseData(window, shell), shell) { // Time in milliseconds before the script timeout handler kicks in. // ■ ここを 10000 から 1000 にする setTimeoutTime(1000); GlobalPropertyInfo staticGlobals[] = { GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly), GlobalPropertyInfo(Identifier(globalExec(), "window"), d()->shell, DontDelete | ReadOnly) }; addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo)); }
3. 次に、 JavaScript が 1 秒以上実行されると、無条件(確認なし)で終了
bool Chrome::shouldInterruptJavaScript() { // ■ この行を追加 return true; // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true); return m_client->shouldInterruptJavaScript(); }
4. alert, confirm, prompt を切る
WebCore/page/DOMWindow.cpp を編集
void DOMWindow::alert(const String& message) { // ■ この行を追加 return; if (!m_frame) return; Document* doc = m_frame->document(); ASSERT(doc); if (doc) doc->updateRendering(); Page* page = m_frame->page(); if (!page) return; page->chrome()->runJavaScriptAlert(m_frame, message); } bool DOMWindow::confirm(const String& message) { // ■ この行を追加 return false; if (!m_frame) return false; Document* doc = m_frame->document(); ASSERT(doc); if (doc) doc->updateRendering(); Page* page = m_frame->page(); if (!page) return false; return page->chrome()->runJavaScriptConfirm(m_frame, message); } String DOMWindow::prompt(const String& message, const String& defaultValue) { // ■ この行を追加 return String(); if (!m_frame) return String(); Document* doc = m_frame->document(); ASSERT(doc); if (doc) doc->updateRendering(); Page* page = m_frame->page(); if (!page) return String(); String returnValue; if (page->chrome()->runJavaScriptPrompt(m_frame, message, defaultValue, returnValue)) return returnValue; return String(); }
5. window.open, window.showModalDialog によるポップアップを切る
WebCore/bindings/js/JSDOMWindowBase.cpp を編集
static bool allowPopUp(ExecState* exec) { // ■ この行を追加 return false; Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); ASSERT(frame); if (frame->script()->processingUserGesture()) return true; Settings* settings = frame->settings(); return settings && settings->JavaScriptCanOpenWindowsAutomatically(); }
補足:ここまでの diff です
Index: WebCore/bindings/js/JSDOMWindowBase.cpp =================================================================== --- WebCore/bindings/js/JSDOMWindowBase.cpp (revision 37703) +++ WebCore/bindings/js/JSDOMWindowBase.cpp (working copy) @@ -175,7 +175,7 @@ : JSGlobalObject(structure, new JSDOMWindowBaseData(window, shell), shell) { // Time in milliseconds before the script timeout handler kicks in. - setTimeoutTime(10000); + setTimeoutTime(1000); GlobalPropertyInfo staticGlobals[] = { GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly), @@ -223,6 +223,8 @@ static bool allowPopUp(ExecState* exec) { + return false; + Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); ASSERT(frame); Index: WebCore/page/DOMWindow.cpp =================================================================== --- WebCore/page/DOMWindow.cpp (revision 37703) +++ WebCore/page/DOMWindow.cpp (working copy) @@ -469,6 +469,8 @@ void DOMWindow::alert(const String& message) { + return; + if (!m_frame) return; @@ -486,6 +488,8 @@ bool DOMWindow::confirm(const String& message) { + return false; + if (!m_frame) return false; @@ -503,6 +507,8 @@ String DOMWindow::prompt(const String& message, const String& defaultValue) { + return String(); + if (!m_frame) return String(); Index: WebCore/page/Chrome.cpp =================================================================== --- WebCore/page/Chrome.cpp (revision 37703) +++ WebCore/page/Chrome.cpp (working copy) @@ -302,6 +302,8 @@ bool Chrome::shouldInterruptJavaScript() { + return true; + // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true);
6. ビルドします
今回は Gtk を使う例です。
$ cd WebKit $ WebKitTools/Scripts/update-webkit $ WebKitTools/Scripts/set-webkit-configuration --release $ WebKitTools/Scripts/build-webkit --gtk
Configure の途中で「bison がないよ><」とか「flex がないよ><」とかいろいろ言われると思いますので、その都度インストールしてくださいね。
7. 起動
さあ、完成です。簡単ですね!
さっそく起動しましょう。
$ WebKitBuild/Release/Programs/GtkLaucher
起動しました!
このブラウザで様々なページを見てみてください!
どんなに重いページでも JavaScript が 1 秒以上連続で実行されませんね。
さらに様々なポップアップや alert 系のウィンドウも出ません。
ばっちりですね!
その他のこと
ただ、これだけで安心という訳ではありません。
いちおう Cookie や Storage 関係も切っておいたほうがいいでしょう。
Storage は build-webkit で --no-storage とか指定すればできます。
まとめ
今日は初めてブラウザのコードに手を入れた記念日。