webkit 加载plugin的过程分析(原)

--------------------------

一,调用过程: (1)首先,看一下webkit在load一个plugin时候的调用栈:

 #0  WebCore::PluginPackage::load (this=0x9ed7868) at WebCore/plugins/gtk/PluginPackageGtk.cpp:142
 #1  WebCore::PluginPackage::fetchInfo (this=0x9ed7868) at WebCore/plugins/gtk/PluginPackageGtk.cpp:68
 #2  WebCore::PluginPackage::createPackage (path=, lastModified=@0xbffd6458: 1264400336) at WebCore/plugins/PluginPackage.cpp:149
 #3  WebCore::PluginDatabase::refresh (this=0x9ed6d60) at WebCore/plugins/PluginDatabase.cpp:111
 #4  WebCore::PluginDatabase::installedPlugins () at WebCore/plugins/PluginDatabase.cpp:46
 #5  WebKit::FrameLoaderClient::objectContentType (this=0x9e6c460, url=, mimeType=) at WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp:352
 #6  WebCore::RenderPartObject::updateWidget (this=0x9eec744, onlyCreateNonNetscapePlugins=true) at WebCore/rendering/RenderPartObject.cpp:217
 #7  WebCore::HTMLObjectElement::updateWidget (this=0x9eebbf8) at WebCore/html/HTMLObjectElement.cpp:191
 #8  WebCore::HTMLPlugInElement::updateWidgetCallback (n=0x9eebbf8) at WebCore/html/HTMLPlugInElement.cpp:213
 #9  WebCore::ContainerNode::dispatchPostAttachCallbacks () at WebCore/dom/ContainerNode.cpp:640
 #10  WebCore::ContainerNode::resumePostAttachCallbacks () at WebCore/dom/ContainerNode.cpp:619
 #11  WebCore::Document::recalcStyle (this=0x9e83798, change=WebCore::Node::NoChange) at WebCore/dom/Document.cpp:1159
 #12  WebCore::Document::updateRendering (this=0x30) at WebCore/dom/Document.cpp:1172
 #13  WebCore::Document::updateDocumentsRendering () at WebCore/dom/Document.cpp:1182
 #14  WebCore::EventTarget::dispatchGenericEvent (this=0x9e837c4, referenceNode=0x9e83798, e=, tempEvent=false) at WebCore/dom/EventTarget.cpp:270
 #15  WebCore::EventTargetNode::dispatchEvent (this=0x9e83798, e=, ec=@0xbffd6988: 0, tempEvent=<value optimized out>) at WebCore/dom/EventTargetNode.cpp:135
 #16  WebCore::Document::finishedParsing (this=0x9e83798) at WebCore/dom/Document.cpp:3721
 #17  WebCore::HTMLParser::finished (this=0x9ec8be8) at WebCore/html/HTMLParser.cpp:1544
 #18  WebCore::HTMLTokenizer::end (this=0x9ee87b0) at WebCore/html/HTMLTokenizer.cpp:1833
 #19  WebCore::HTMLTokenizer::finish (this=0x9ee87b0) at WebCore/html/HTMLTokenizer.cpp:1873
 #20  WebCore::Document::finishParsing (this=0x9e83798) at WebCore/dom/Document.cpp:1687
 #21  WebCore::FrameLoader::endIfNotLoadingMainResource (this=0x9e6c924) at WebCore/loader/FrameLoader.cpp:1075
 #22  WebCore::FrameLoader::end (this=0x9e6c924) at WebCore/loader/FrameLoader.cpp:1059
 #23  WebCore::DocumentLoader::finishedLoading (this=0x9eb2428) at WebCore/loader/DocumentLoader.cpp:343
 #24  WebCore::FrameLoader::finishedLoading (this=0x9e6c924) at WebCore/loader/FrameLoader.cpp:2931
 #25  WebCore::MainResourceLoader::didFinishLoading (this=0x9eb3a48) at WebCore/loader/MainResourceLoader.cpp:320
 #26  WebCore::ResourceLoader::didFinishLoading (this=0x9eb3a48) at WebCore/loader/ResourceLoader.cpp:389
 #27  WebCore::ResourceHandleManager::downloadTimerCallback (this=0x9eb4560, timer=0x9eb4560) at WebCore/platform/network/curl/ResourceHandleManager.cpp:298
 #28  WebCore::Timer<WebCore::ResourceHandleManager>::fired (this=0x9eb4560) at ./WebCore/platform/Timer.h:99
 #29  WebCore::TimerBase::fireTimers (fireTime=1269409078.032583, firingTimers=) at WebCore/platform/Timer.cpp:347
 #30  WebCore::TimerBase::sharedTimerFired () at WebCore/platform/Timer.cpp:368
 #31  timeout_cb () at WebCore/platform/gtk/SharedTimerGtk.cpp:48
 #32  ?? () at :0
 #33  g_main_context_dispatch () at :0
 #34  ?? () at :0
 #35  g_main_loop_run () at :0
 #36  gtk_main () at :0
 #37  main (argc=4, argv=0xbffd7134) at WebKitTools/GtkLauncher/main.cpp:52}}}

从第8层(#8)向第0层(#0)依次说起,

#8 中:

 void HTMLPlugInElement::updateWidgetCallback(Node* n)
 {
  static_cast<HTMLPlugInElement*>(n)->updateWidget();
 }

#7 中:

 void HTMLObjectElement::updateWidget()
 {
  document()->updateRendering();
  if (m_needWidgetUpdate && renderer() && !m_useFallbackContent && !isImageType())
   static_cast<RenderPartObject*>(renderer())->updateWidget(true); //在这里,调用了 RenderPartObject::updateWidget 函数
 }

#6 中:(这个函数很复杂,只截取对我们有用的部分来)

 void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins)
 {
  ....
      if (onlyCreateNonNetscapePlugins) {
          KURL completedURL;
          if (!url.isEmpty())
              completedURL = frame->loader()->completeURL(url);
        
         //这里,就是要判断 objectContentType,而在判断这个 objectContentType的过程中,发生了很多事,下面会详细解释
          if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin)
              return;
       }   
  ....
 }

#5 中 :

 ObjectContentType FrameLoaderClient::objectContentType(const KURL& url, const String& mimeType)
 {
  String type = mimeType;
  // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns "application/octet-stream" upon failure
  if (type.isEmpty())
      type = MIMETypeRegistry::getMIMETypeForExtension(url.path().substring(url.path().reverseFind('.') + 1));

  if (type.isEmpty())
      return WebCore::ObjectContentFrame;

  if (MIMETypeRegistry::isSupportedImageMIMEType(type))
      return WebCore::ObjectContentImage;
  
  //这里,要寻找已经安装过的 plugin,
  if (PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType))
      return WebCore::ObjectContentNetscapePlugin;

  if (MIMETypeRegistry::isSupportedNonImageMIMEType(type))
      return WebCore::ObjectContentFrame;

  return WebCore::ObjectContentNone;
 }

#4 中 :

 PluginDatabase* PluginDatabase::installedPlugins()
 {
  static PluginDatabase* plugins = 0;
  
  if (!plugins) {
      plugins = new PluginDatabase; //新建一个 plugin的集合
      plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories()); //设置它的搜索路径,defaultPluginDirectories函数中默认写了很多系统路径
      
      //这里将要刷新plugin,继续往下看
      plugins->refresh();
  }

  return plugins;
 }

#3 中 :

 //这个函数比较长, 只分析有用的部分, 
 //函数的主要过程是: 
 //1,Unload plugins, 
 //2,检查所有路径中的 plugin,如果从上次refresh 到现在,没有改变过(检验时间戳),那么跳过它,
 //   否则,就要 建立 PluginPackage(重点分析这里),
 //3, 注册 plug-in MIME types
 bool PluginDatabase::refresh()
 {   
  bool pluginSetChanged = false;

  
  if (!m_plugins.isEmpty()) {
      PluginSet pluginsToUnload;
      getDeletedPlugins(pluginsToUnload);

      // Unload plugins
      PluginSet::const_iterator end = pluginsToUnload.end();
      for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it)
          remove(it->get());

      pluginSetChanged = !pluginsToUnload.isEmpty();
  }

  HashSet<String> paths;
  getPluginPathsInDirectories(paths);

  HashMap<String, time_t> pathsWithTimes;

  // We should only skip unchanged files if we didn't remove any plugins above. If we did remove
  // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions
  // of RealPlayer installed and just removed the newer one, we'll pick up the older one.
  bool shouldSkipUnchangedFiles = !pluginSetChanged;

  HashSet<String>::const_iterator pathsEnd = paths.end();
  for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) {
      time_t lastModified;
      if (!getFileModificationTime(*it, lastModified))
          continue;

      pathsWithTimes.add(*it, lastModified);

      // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything.
      if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified)
          continue;

      if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) {
          ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified);
          remove(oldPackage.get());
      }
   
   //这里建立一个新的 PluginPackage
      RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified);
      if (package && add(package.release()))
          pluginSetChanged = true;
  }

  // Cache all the paths we found with their timestamps for next time.
  pathsWithTimes.swap(m_pluginPathsWithTimes);

  if (!pluginSetChanged)
      return false;

  m_registeredMIMETypes.clear();

  // Register plug-in MIME types
  PluginSet::const_iterator end = m_plugins.end();
  for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) {
      // Get MIME types
      MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end();
      for (MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin(); map_it != map_end; ++map_it) {
          m_registeredMIMETypes.add(map_it->first);
      }
  }

  return true;
 }

#2 中 :

 PassRefPtr<PluginPackage> PluginPackage::createPackage(const String& path, const time_t& lastModified)
 {
  //建立一个 PluginPackage ,具体过程待具体分析,
  RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified));

  //获得 PluginPackage 的信息,重点看这里
  if (!package->fetchInfo())
      return 0;
  
  return package.release();
 }

#1 中:

 //这里就是插件初始化的过程了,
 //过程下面将解释
 bool PluginPackage::fetchInfo()
 {
 #if defined(XP_UNIX)
  if (!load()) //这里 load 一个 PluginPackage,实质上的作用是 给PluginPackage类的如下成员赋值:m_pluginFuncs, m_module
      return false;

  NP_GetMIMEDescriptionFuncPtr NP_GetMIMEDescription;
  NPP_GetValueProcPtr NPP_GetValue;

  //通过上面的load 过程,m_module已经指向了一个 so文件(也就是我们的plugin)
  //这里 找到 这个插件中实现的 NP_GetMIMEDescription 和 NP_GetValue 函数
  g_module_symbol(m_module, "NP_GetMIMEDescription", (void**)&NP_GetMIMEDescription);
  g_module_symbol(m_module, "NP_GetValue", (void**)&NPP_GetValue);


  //例如 types = application/x-java-vm:jar,jad:IPTV MIDP Application; .........
  //这里分解 types中的内容,并保存
  const gchar* types = NP_GetMIMEDescription();
  gchar** mimeDescs = g_strsplit(types, ";", -1);
  for (int i = 0; mimeDescs[i] && mimeDescs[i][0]; i++) {
      gchar** mimeData = g_strsplit(mimeDescs[i], ":", 3);


      String description = String::fromUTF8(mimeData[2]);
      gchar** extensions = g_strsplit(mimeData[1], ",", -1);

      Vector<String> extVector;
      for (int j = 0; extensions[j]; j++)
          extVector.append(String::fromUTF8(extensions[j]));

      determineQuirks(mimeData[0]);
      m_mimeToExtensions.add(mimeData[0], extVector);
      m_mimeToDescriptions.add(mimeData[0], description);

      g_strfreev(extensions);
      g_strfreev(mimeData);
  }
  g_strfreev(mimeDescs);

  char* buffer = 0;
  //调用 插件中的GetValue 方法,得到 name
  NPError err = NPP_GetValue(0, NPPVpluginNameString, &buffer);
  if (err == NPERR_NO_ERROR)
      m_name = buffer;

  buffer = 0;
  //调用 插件中的GetValue 方法,得到 description
  err = NPP_GetValue(0, NPPVpluginDescriptionString, &buffer);
  if (err == NPERR_NO_ERROR)
      m_description = buffer;

  return true;
 #else
  notImplemented();
  return false;
 #endif
 }

#0 中:

 bool PluginPackage::load()
 {
  if (m_isLoaded) {
      m_loadCount++;
      return true;
  }

  //调用 g_module_open 打开一个 plugin, 实质上这个函数是gtk里面用来打开库文件的, 这里将打开例如 /usr/lib/mozilla/plugins/libmidplugin.so 
  m_module = g_module_open((m_path.utf8()).data(), G_MODULE_BIND_LOCAL);

  if (!m_module) {
      LOG(Plugin,"Module Load Failed :%s, Error:%s\n", (m_path.utf8()).data(), g_module_error());
      return false;
  }

  m_isLoaded = true;

  NP_InitializeFuncPtr NP_Initialize;
  NPError npErr;

  //函数g_module_symble来取得插件中的函数指针,调用这一函数的前题是我们声明的函数指针的格式和插件中的定义的函数的格式要相同
  //这里就是把 函数指针 (void**)&NP_Initialize 对应到 /usr/lib/mozilla/plugins/libmidplugin.so 中已经实现的函数 NP_Initialize
  g_module_symbol(m_module, "NP_Initialize", (void**)&NP_Initialize);
  //同上,
  g_module_symbol(m_module, "NP_Shutdown", (void**)&m_NPP_Shutdown);

  if (!NP_Initialize || !m_NPP_Shutdown)
      goto abort;

  memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
  m_pluginFuncs.size = sizeof(m_pluginFuncs);

  //给 m_browserFuncs 的各个成员赋值
  m_browserFuncs.size = sizeof (m_browserFuncs);
  m_browserFuncs.version = NP_VERSION_MINOR;
  m_browserFuncs.geturl = NPN_GetURL;
  m_browserFuncs.posturl = NPN_PostURL;
  m_browserFuncs.requestread = NPN_RequestRead;
  m_browserFuncs.newstream = NPN_NewStream;
  m_browserFuncs.write = NPN_Write;
  m_browserFuncs.destroystream = NPN_DestroyStream;
  m_browserFuncs.status = NPN_Status;
  m_browserFuncs.uagent = NPN_UserAgent;
  m_browserFuncs.memalloc = NPN_MemAlloc;
  m_browserFuncs.memfree = NPN_MemFree;
  m_browserFuncs.memflush = NPN_MemFlush;
  m_browserFuncs.reloadplugins = NPN_ReloadPlugins;
  m_browserFuncs.geturlnotify = NPN_GetURLNotify;
  m_browserFuncs.posturlnotify = NPN_PostURLNotify;
  m_browserFuncs.getvalue = NPN_GetValue;
  m_browserFuncs.setvalue = NPN_SetValue;
  m_browserFuncs.invalidaterect = NPN_InvalidateRect;
  m_browserFuncs.invalidateregion = NPN_InvalidateRegion;
  m_browserFuncs.forceredraw = NPN_ForceRedraw;
  m_browserFuncs.getJavaEnv = NPN_GetJavaEnv;
  m_browserFuncs.getJavaPeer = NPN_GetJavaPeer;
  m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
  m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;

  m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
  m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
  m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
  m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
  m_browserFuncs.identifierisstring = _NPN_IdentifierIsString;
  m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
  m_browserFuncs.createobject = _NPN_CreateObject;
  m_browserFuncs.retainobject = _NPN_RetainObject;
  m_browserFuncs.releaseobject = _NPN_ReleaseObject;
  m_browserFuncs.invoke = _NPN_Invoke;
  m_browserFuncs.invokeDefault = _NPN_InvokeDefault;
  m_browserFuncs.evaluate = _NPN_Evaluate;
  m_browserFuncs.getproperty = _NPN_GetProperty;
  m_browserFuncs.setproperty = _NPN_SetProperty;
  m_browserFuncs.removeproperty = _NPN_RemoveProperty;
  m_browserFuncs.hasproperty = _NPN_HasMethod;
  m_browserFuncs.hasmethod = _NPN_HasProperty;
  m_browserFuncs.setexception = _NPN_SetException;
  m_browserFuncs.enumerate = _NPN_Enumerate;
  //赋值完成
  
  
  //初始化 plugin(调用 *.so 中已经实现的初始化方法)
 #if defined(XP_UNIX)
  npErr = NP_Initialize(&m_browserFuncs, &m_pluginFuncs);
 #else
  npErr = NP_Initialize(&m_browserFuncs);
 #endif
  if (npErr != NPERR_NO_ERROR)
      goto abort;

  m_loadCount++;
  return true;

 abort:
  unloadWithoutShutdown();
  return false;
 }

至此, 分析了webkit在加载一个 plugin 时候所做的一些操作, 过程都是通过调试器查看调用栈,分析源代码完成的,其中难免会有错误, 如果有任何好的建议,请马上留下


转自http://zxw0521.blog.163.com/blog/static/743535620102301302750/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值