本过程分析基于android4.3(webkit NPAPI),js引擎为v8。不同Android版本的区别
WebView包含两个部分,一部分是上层的Java代码(/frameworks/base/core/java/android/webkit),包括若干Java类,用于对外提供接口;另一部分是下层的C++代码(/external/webkit),包括两个so库(libwebcore.so和libchromium_net.so),用于网页的解析和渲染。两个部分之间通过JNI进行交互。
// /framework/base/core/java/android/webkit/JniUtil.java
30 static {
31 System.loadLibrary("webcore");
32 System.loadLibrary("chromium_net");
33 }
这段代码指定了所使用的动态链接库,即libwebcore.so和libchromium_net.so。
JAVA -> JS
// /external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
1573 static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
1574 jobject javascriptObj, jstring interfaceName, jboolean requireAnnotation)
1575 {
1576 WebCore::Frame* pFrame = 0;
1577 if (nativeFramePointer == 0)
1578 pFrame = GET_NATIVE_FRAME(env, obj);
1579 else
1580 pFrame = (WebCore::Frame*)nativeFramePointer;
1581 ALOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!"); 1582
1583 JavaVM* vm;
1584 env->GetJavaVM(&vm);
1585 ALOGV("::WebCore:: addJSInterface: %p", pFrame);
1586
1587 if (pFrame) {
1588 RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj,
1589 requireAnnotation);
1590 const char* name = getCharactersFromJStringInEnv(env, interfaceName);
1591 // Pass ownership of the added object to bindToWindowObject.
1592 NPObject* npObject = JavaInstanceToNPObject(addedObject.get());
1593 pFrame->script()->bindToWindowObject(pFrame, name, npObject);
1594 // bindToWindowObject calls NPN_RetainObject on the
1595 // returned one (see createV8ObjectForNPObject in V8NPObject.cpp).
1596 // bindToWindowObject also increases obj's ref count and decreases
1597 // the ref count when the object is not reachable from JavaScript
1598 // side. Code here must release the reference count increased by
1599 // bindToWindowObject.
1600
1601 // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds
1602 // we use WebCore/bindings/v8/npruntime.cpp (rather than
1603 // WebCore/bridge/npruntime.cpp), so the function is implemented there.
1604 // TODO: Combine the two versions of these NPAPI files.
1605 NPN_ReleaseObject(npObject);
1606 releaseCharactersForJString(interfaceName, name);
1607 }
1608 }
该函数中,从java环境中传入的jobject javascriptObj先转化为WeakJavaInstance addedObject(WeakJavaInstance由JavaInstanceJobject派生而来)对象,再由NPObject* JavaInstanceToNPObject(JavaInstance* instance)函数转为NPObject npObject(之后分析),最后使用void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)方法将npObject绑定至javascript上下文中。
- 注:
v8引擎沿用了一部分javascriptCore的代码。从上面源代码的最后一段注释可以了解到这个有趣的现象。
NPObject是NPAPI中重要的一个结构体,js中的各种变量等都是包装为这个结构体由javascript引擎直接对其操作。V8使用的v8Object是对其的一个扩充,其中包含了指向NPObject的指针(为了兼容)。
// /external/webkit/Source/WebCore/bridge/npruntime.h
323 struct NPObject {
324 NPClass *_class;
325 uint32_t referenceCount;
326 /*
327 * Additional space may be allocated here by types of NPObjects
328 */
329 };
NPObject由一个指向NPClass的指针和引用计数组成。
// /external/webkit/Source/WebCore/bridge/npruntime.h
295 struct NPClass
296 {
297 uint32_t structVersion;
298 NPAllocateFunctionPtr allocate;
299 NPDeallocateFunctionPtr deallocate;
300 NPInvalidateFunctionPtr invalidate;
301 NPHasMethodFunctionPtr hasMethod;
302 NPInvokeFunctionPtr invoke;
303 NPInvokeDefaultFunctionPtr invokeDefault;
304 NPHasPropertyFunctionPtr hasProperty;
305 NPGetPropertyFunctionPtr getProperty;
306 NPSetPropertyFunctionPtr setProperty;
307 NPRemovePropertyFunctionPtr removeProperty;
308 NPEnumerationFunctionPtr enumerate;
309 NPConstructFunctionPtr construct;
310 };
除了structVersion外都是指向这些功能函数的指针。
需要注意的是将JavaInstance转为NPObject时对这个NPObject的NPClass做了一个针对java的特殊的初始化,初始化为JavaNPObjectClass。
// /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
76 NPObject* JavaInstanceToNPObject(JavaInstance* instance)
77 {
78 JavaNPObject* object = reinterpret_cast<JavaNPObject*>(_NPN_CreateObject(0,&JavaNPObjectClass));
79 object->m_instance = instance;
80 return reinterpret_cast<NPObject*>(object);
81 }
...
60 static NPClass JavaNPObjectClass = {
61 NP_CLASS_STRUCT_VERSION,
62 AllocJavaNPObject, // allocate,
63 FreeJavaNPObject, // free,
64 0, // invalidate
65 JavaNPObjectHasMethod,
66 JavaNPObjectInvoke,
67 0, // invokeDefault,
68 JavaNPObjectHasProperty,
69 JavaNPObjectGetProperty,
70 0, // setProperty
71 0, // removeProperty
72 0, // enumerate
73 0 // construct
74 };
其中JavaNPObjectInvoke指向的函数用于在JS中调用JAVA方法时将所需信息传递回JAVA环境,并调用反射机制执行。JavaNPObjectClass也表明了JS能对该JAVA对象执行哪些操作。
// /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
110 bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result)
111 {
112 JavaInstance* instance = ExtractJavaInstance(obj);
113 if (!instance)
114 return false;
115 NPUTF8* name = _NPN_UTF8FromIdentifier(identifier);
116 if (!name)
117 return false;
118
119 instance->begin();
120
121 MethodList methodList = instance->getClass()->methodsNamed(name);
122 // TODO: use NPN_MemFree
123 free(name);
124
125 // Try to find a good match for the overloaded method. The
126 // fundamental problem is that JavaScript doesn't have the
127 // notion of method overloading and Java does. We could
128 // get a bit more sophisticated and attempt to do some
129 // type checking as well as checking the number of parameters.
130 size_t numMethods = methodList.size();
131 JavaMethod* aMethod;
132 JavaMethod* jMethod = 0;
133 for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) {
134 aMethod = methodList[methodIndex];
135 if (aMethod->numParameters() == static_cast<int>(argCount)) {
136 jMethod = aMethod;
137 break;
138 }
139 }
140 if (!jMethod) {
141 instance->end();
142 return false;
143 }
144
145 JavaValue* jArgs = new JavaValue[argCount];
146 for (unsigned int i = 0; i < argCount; i++)
147 jArgs[i] = convertNPVariantToJavaValue(args[i], jMethod->parameterAt(i));
148
149 // ANDROID
150 bool exceptionOccurred;
151 JavaValue jResult = instance->invokeMethod(jMethod, jArgs, exceptionOccurred);
152 instance->end();
153 delete[] jArgs;
154
155 if (exceptionOccurred)
156 return false;
157 // END ANDROID
158
159 VOID_TO_NPVARIANT(*result);
160 convertJavaValueToNPVariant(jResult, result);
161 return true;
162 }
这个函数会将传入的NPAPI的结构体经过转换后,调用JAVA中的反射机制执行并获取返回结果,也就是在NPAPI和JAVA之间起了桥梁作用。从JS -> JAVA方向进行分析时,最后也会回到这个函数。
Line130~143为将反射的方法与方法列表进行比较(反射机制相关)。
最后分析一下 pFrame->script()->bindToWindowObject(pFrame, name, npObject);
// /external/webkit/Source/WebCore/bindings/v8/ScriptController.cpp
262 // Create a V8 object with an interceptor of NPObjectPropertyGetter.
263 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)
264 {
265 v8::HandleScope handleScope;
266
267 v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
268 if (v8Context.IsEmpty())
269 return;
270
271 v8::Context::Scope scope(v8Context);
272
273 v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0);
274
275 // Attach to the global object.
276 v8::Handle<v8::Object> global = v8Context->Global();
277 global->Set(v8String(key), value);
278 }
在该函数中首先将之前传过来的npObject(此处为object)转变为V8引擎所使用的V8Object(该结构体声明在:/external/webkit/Source/WebCore/bindings/v8/NPV8Object.h)。最后将该V8Object的句柄附加到globle中,即可在js上下文中调用。
- createV8ObjectForNPObject(…)方法位于/external/webkit/Source/WebCore/bindings/v8/V8NPObject.cpp
JS -> JAVA
// /external/webkit/Source/WebCore/bindings/v8/V8NPObject.cpp
69 static v8::Handle<v8::Value> npObjectInvokeImpl(const v8::Arguments& args, InvokeFunctionType functionId)
70 {
71 NPObject* npObject;
72
73 // These three types are subtypes of HTMLPlugInElement.
74 if (V8HTMLAppletElement::HasInstance(args.Holder()) || V8HTMLEmbedElement::HasInstance(args.Holder())
75 || V8HTMLObjectElement::HasInstance(args.Holder())) {
76 // The holder object is a subtype of HTMLPlugInElement.
77 HTMLPlugInElement* element;
78 if (V8HTMLAppletElement::HasInstance(args.Holder()))
79 element = V8HTMLAppletElement::toNative(args.Holder());
80 else if (V8HTMLEmbedElement::HasInstance(args.Holder()))
81 element = V8HTMLEmbedElement::toNative(args.Holder());
82 else
83 element = V8HTMLObjectElement::toNative(args.Holder());
84 ScriptInstance scriptInstance = element->getInstance();
85 if (scriptInstance)
86 npObject = v8ObjectToNPObject(scriptInstance->instance());
87 else
88 npObject = 0;
89 } else {
90 // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three
91 // internal fields.
92 if (args.Holder()->InternalFieldCount() != npObjectInternalFieldCount)
93 return throwError("NPMethod called on non-NPObject", V8Proxy::ReferenceError);
94
95 npObject = v8ObjectToNPObject(args.Holder());
96 }
97
98 // Verify that our wrapper wasn't using a NPObject which has already been deleted.
99 if (!npObject || !_NPN_IsAlive(npObject))
100 return throwError("NPObject deleted", V8Proxy::ReferenceError);
101
102 // Wrap up parameters.
103 int numArgs = args.Length();
104 OwnArrayPtr<NPVariant> npArgs = adoptArrayPtr(new NPVariant[numArgs]);
105
106 for (int i = 0; i < numArgs; i++)
107 convertV8ObjectToNPVariant(args[i], npObject, &npArgs[i]);
108
109 NPVariant result;
110 VOID_TO_NPVARIANT(result);
111
112 bool retval = true;
113 switch (functionId) {
114 case InvokeMethod:
115 if (npObject->_class->invoke) {
116 v8::Handle<v8::String> functionName(v8::String::Cast(*args.Data()));
117 NPIdentifier identifier = getStringIdentifier(functionName);
118 retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result);
119 }
120 break;
121 case InvokeConstruct:
122 if (npObject->_class->construct)
123 retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result);
124 break;
125 case InvokeDefault:
126 if (npObject->_class->invokeDefault)
127 retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result);
128 break;
129 default:
130 break;
131 }
132
133 if (!retval)
134 throwError("Error calling method on NPObject.", V8Proxy::GeneralError);
135
136 for (int i = 0; i < numArgs; i++)
137 _NPN_ReleaseVariantValue(&npArgs[i]);
138
139 // Unwrap return values.
140 v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject);
141 _NPN_ReleaseVariantValue(&result);
142
143 return returnValue;
144 }
这个函数用于将JS中获取的JAVA对象和JS格式的参数(JAVA反射机制将使用这个对象和参数)转换为NPAPI中通用的结构体,并调用对应的Invoke函数,在Invoke函数中进一步处理后交由JAVA环境处理,也就是在JS和NPAPI中起到了一个桥梁作用。
Line106~107为参数打包,Line113~131为根据标识码调用对应的Invoke函数。
- 参数打包使用的函数在 /external/webkit/Source/WebCore/bindings/v8/V8NPUtils.cpp 中
- Invoke函数在 /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp 中,也就是上面提到的bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result)
未完待续…