webkit 中 javascript 与 WebCore DOM 的绑定
来源:互联网 发布:java小项目开发案例 编辑:程序博客网 时间:2024/05/24 06:12
转载请注明出处:http://blog.csdn.net/awebkit
由于工作中需要调试 JavaScript 的时候并不过,我对 WebKit 中 JavaScript 的了解并不深刻,我只能对 JavaScript 与 WebCore DOM 之间的接口进行一番解释。
如有错误,欢迎指正。
JavaScript 的基础知识
我们先来了解一下 JavaScript Engine 到底是什么。
通俗的讲,JavaScript Engine 就是一个脚本解释器,内置了一些对象如 date 。事实上, JavaScript Engine 只识别对象,可以说我们看到的所有变量都是对象。根据 ECMAScript 标准,JavaScript 提供的对象如下。
仔细看看,你会发现没有 window 对象。惊讶吗?没错,确实没有 window 对象。那么,对于 JavaScript:window.open ,JavaScript Engine 又是如何解释的呢?
这里就涉及到了 JavaScript Engine 与 DOM 的绑定。
总结一下就是 JavaScript Engine 作为一个 Script Engine ,只提供了很简单的一些功能,但是他又提供了一种扩展,可以把其他扩展对象加入到这个 Script Engine 里面,让这个 Script Engine 能解释这些新对象。其中,对 DOM 的操作就是通过扩展来实现的,又叫 JavaScript 与 DOM 的绑定。
DOM 绑定时机
DOM 是在什么时候绑定的呢?
我们知道 script 对应 frame 。frame 里面的 ScriptController 是运行 script 的关键类。在遇到 script 标签需要运行script 的时候,会调用 ScriptController 的 initScript ,从名字可以看出来,这是初始化的操作。那么,都做了哪些初始化操作呢?我们看一下代码
JSDOMWindowShell* ScriptController::initScript(DOMWrapperWorld* world){ ASSERT(!m_windowShells.contains(world)); JSLock lock(SilenceAssertionsOnly); JSDOMWindowShell* windowShell = createWindowShell(world); windowShell->window()->updateDocument(); if (Page* page = m_frame->page()) { attachDebugger(windowShell, page->debugger()); windowShell->window()->setProfileGroup(page->group().identifier()); } m_frame->loader()->dispatchDidClearWindowObjectInWorld(world); return windowShell;}
先来分析 createWindowShell
JSDOMWindowShell* ScriptController::createWindowShell(DOMWrapperWorld* world){ ASSERT(!m_windowShells.contains(world)); Global<JSDOMWindowShell> windowShell(*world->globalData(), new JSDOMWindowShell(m_frame->domWindow(), world)); Global<JSDOMWindowShell> windowShell2(windowShell); m_windowShells.add(world, windowShell); world->didCreateWindowShell(this); return windowShell.get();}
JSDOMWindowShell::JSDOMWindowShell(PassRefPtr<DOMWindow> window, DOMWrapperWorld* world) : Base(JSDOMWindowShell::createStructure(*world->globalData(), jsNull())) , m_world(world){ ASSERT(inherits(&s_info)); setWindow(window);}
void JSDOMWindowShell::setWindow(PassRefPtr<DOMWindow> domWindow){ // Explicitly protect the global object's prototype so it isn't collected // when we allocate the global object. (Once the global object is fully // constructed, it can mark its own prototype.) RefPtr<Structure> prototypeStructure = JSDOMWindowPrototype::createStructure(*JSDOMWindow::commonJSGlobalData(), jsNull()); Global<JSDOMWindowPrototype> prototype(*JSDOMWindow::commonJSGlobalData(), new JSDOMWindowPrototype(0, prototypeStructure.release())); RefPtr<Structure> structure = JSDOMWindow::createStructure(*JSDOMWindow::commonJSGlobalData(), prototype.get()); JSDOMWindow* jsDOMWindow = new (JSDOMWindow::commonJSGlobalData()) JSDOMWindow(structure.release(), domWindow, this); prototype->putAnonymousValue(*JSDOMWindow::commonJSGlobalData(), 0, jsDOMWindow); setWindow(*JSDOMWindow::commonJSGlobalData(), jsDOMWindow);}
代码很啰唆,可以只看我标记为黑体的部分,先理清一条线。
我们看到 createWindowShell 里面会创建 JSDOMWindow 。这是一个非常重要的类,对应了 DOMWindow的 JavaScript 类。我们看到所有的 JSXXX 对应的都是绑定JavaScript 与 XXX 关系的类,而且这部分类大都(除了bindings下面的 JSXXX 类)是根据 IDL 和脚本自动生成的(牛掰啊)。所以,我们可以看到 window.open 会先调用 JSDOMWindow ,然后调用到 DOMWindow 。JSDOMWindow 构造没什么好说的,继承于 JSDOMWindowBase ,我们看一下 JSDOMWindowBase 吧。
JSDOMWindowBase::JSDOMWindowBase(NonNullPassRefPtr<Structure> structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) : JSDOMGlobalObject(structure, shell->world(), shell) , m_impl(window) , m_shell(shell){ ASSERT(inherits(&s_info)); GlobalPropertyInfo staticGlobals[] = { GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly), GlobalPropertyInfo(Identifier(globalExec(), "window"), m_shell, DontDelete | ReadOnly) }; addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));}
addStaticGlobals把 window 对象加入到了 JavaScript 执行环境中,对应 JSDOMWindowShell (是个 JSObject 对象),从此,window 再也不是未定义了。
关于 addStaticGlobals 的详细介绍以后再说。你只要了解这个类把一些 JSObject 对象添加到了 JavaScript 执行环境中就可以了。
再回到开始的地方,还有一个标记黑色的函数 updateDocument,代码如下
void JSDOMWindowBase::updateDocument(){ ASSERT(m_impl->document()); ExecState* exec = globalExec(); symbolTablePutWithAttributes(exec->globalData(), Identifier(exec, "document"), toJS(exec, this, m_impl->document()), DontDelete | ReadOnly);}
其实,就是更新了 document 对应的类。这样,DOM 的两个基本对象(window 和 document)就都有了。
总结一下,ScriptController 在 initScript 中把 window 对象,document 对象添加到了 JavaScript 执行环境中,对window 等全局对象的操作可以找到对应的类。
如何扩展 JavaScript 对象
上面我们讲了 WebKit 是如何在 JavaScript Engine 中扩展 window document 对象的,理解了整个流程,对于我们扩展自己对象就轻车熟路了。可以查看后面的参考。
如果每个对象都像参考里面说的需要修改源码,那就太麻烦了,对于浏览器开发者来说,需要给上层提供扩展对象的接口。就像 android 里面的 addJavaScriptInterface 。
那么,在哪里添加接口呢?
再回到刚开始的地方,我们看到还有一个标记黑体的函数 dispatchDidClearWindowObjectInWorld ,这个首先是 FrameLoader 的函数,然后会走到 FrameLoaderClient 的对应函数。
我们看一下 android 这个函数的实现
// This function is used to re-attach Javascript<->native code classes.void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world){ if (world != mainThreadNormalWorld()) return; ASSERT(m_frame); LOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n", m_frame, m_frame->loader()->url().string().ascii().data()); m_webFrame->windowObjectCleared(m_frame);}
经过 JNI 代码,调用到 java 部分 BrowserFrame 的代码。(请注意解释)
/* * This method is called by WebCore to inform the frame that * the Javascript window object has been cleared. * We should re-attach any attached js interfaces. */ private void windowObjectCleared(int nativeFramePointer) { Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); while (iter.hasNext()) { String interfaceName = iter.next(); Object object = mJavaScriptObjects.get(interfaceName); if (object != null) { nativeAddJavascriptInterface(nativeFramePointer, mJavaScriptObjects.get(interfaceName), interfaceName); } } mRemovedJavaScriptObjects.clear(); stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE); }
nativeAddJavascriptInterface 经过 JNI ,又会调用到 WebCoreFrameBridge::AddJavascriptInterface 函数。这个函数把新扩展的对象加入到 window 对象中。
static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer, jobject javascriptObj, jstring interfaceName){#ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);#endif WebCore::Frame* pFrame = 0; if (nativeFramePointer == 0) pFrame = GET_NATIVE_FRAME(env, obj); else pFrame = (WebCore::Frame*)nativeFramePointer; LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!"); JavaVM* vm; env->GetJavaVM(&vm); const char* myname = getCharactersFromJStringInEnv(env, interfaceName); DBG_NAV_LOGD("::WebCore:: addJSInterface: %p, js %s", pFrame, myname);#if USE(JSC) // Copied from qwebframe.cpp JSC::JSLock lock(JSC::SilenceAssertionsOnly); WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame, mainThreadNormalWorld()); if (window) { RootObject *root = pFrame->script()->bindingRootObject(); setJavaVM(vm); // Add the binding to JS environment JSC::ExecState* exec = window->globalExec(); JSC::JSObject* addedObject = WeakJavaInstance::create(javascriptObj, root)->createRuntimeObject(exec); const jchar* s = env->GetStringChars(interfaceName, NULL); if (s) { // Add the binding name to the window's table of child objects. JSC::PutPropertySlot slot; window->put(exec, JSC::Identifier(exec, (const UChar *)s, env->GetStringLength(interfaceName)), addedObject, slot); env->ReleaseStringChars(interfaceName, s); checkException(env); } }#elif USE(V8) if (pFrame) { RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj); const char* name = getCharactersFromJStringInEnv(env, interfaceName); // Pass ownership of the added object to bindToWindowObject. NPObject* npObject = JavaInstanceToNPObject(addedObject.get()); pFrame->script()->bindToWindowObject(pFrame, name, npObject); // bindToWindowObject calls NPN_RetainObject on the // returned one (see createV8ObjectForNPObject in V8NPObject.cpp). // bindToWindowObject also increases obj's ref count and decreases // the ref count when the object is not reachable from JavaScript // side. Code here must release the reference count increased by // bindToWindowObject. // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds // we use WebCore/bindings/v8/npruntime.cpp (rather than // WebCore/bridge/npruntime.cpp), so the function is implemented there. // TODO: Combine the two versions of these NPAPI files. NPN_ReleaseObject(npObject); releaseCharactersForJString(interfaceName, name); }#endif}
上面我讲了 android 平台如何扩展 JavaScript 对象的,当我们作浏览器开发的时候,可以照猫画虎。
但上面只是讲的流程,如果需要扩展 JavaScript 对象,必须对扩展的对象 JSObject 熟悉,知道 JavaScript 是如何认识对象的,这个以后再讲。
参考:
http://blog.csdn.net/horkychen/article/details/7640052
- webkit 中 javascript 与 WebCore DOM 的绑定
- webkit 与 webcore
- [WebKit]WebCore之页面加载的设计与实现(一)
- [WebKit]WebCore之页面加载的设计与实现(二)
- [WebKit]WebCore之页面加载的设计与实现(三)
- [WebKit] WebCore之页面加载的设计与实现
- WebKit之jsc和webcore的交互
- 关于chromium中几个类的类图:class-diagram-webkit-webcore-to-chrome-browser
- [WebKit] JavaScriptCore解析--基础篇 (一)JSC与WebCore
- [WebKit] JavaScriptCore解析--基础篇 (一)JSC与WebCore
- WebKit之WebCore篇
- WebKit之WebCore
- 浅谈WebKit之WebCore
- WebKit之WebCore篇
- WebCore中dom目录中类层次解析
- Javascript中BOM与DOM
- 浅谈WebKit之WebCore篇
- 浅谈WebKit之WebCore篇
- java中重定向和转发的差别
- 例题3-6
- #遍历E盘下的mp3文件
- UVa 11992 - Fast Matrix Operations 成段更新,求最值与和
- Liferay安装笔记(默认安装使用Mysql数据库)
- webkit 中 javascript 与 WebCore DOM 的绑定
- mysql master slave 搭建
- 为android程序添加背景音乐和Menu菜单
- 获取上一个页面的跳转地址
- C#开源资源大汇总
- 关于ORACLE的UPDATE更新多表的问题
- 转化为ico文件
- oracle 联表 update
- mybatis的参数