mshtml/nsio.c 实现报告
概述
对 dll/win32/mshtml/nsio.c 进行了全面的 XPCOM 接口实现,将原本大量 NS_ERROR_NOT_IMPLEMENTED 的 stub 函数实现为可正常工作的代码。同时修复了一个关键的 vtable 错位 bug,并扩展了 nsChannel 结构体以支持新实现的字段。
涉及修改的文件:
dll/win32/mshtml/nsio.c--- 主要实现dll/win32/mshtml/binding.h--- nsChannel 结构体字段扩展dll/win32/mshtml/nsiface.idl--- 接口定义修复
一、关键 Bug 修复:vtable 偏移
问题
Gecko 2.40(wine_gecko)的 nsIHttpChannelInternal IDL 相比 ReactOS 版本多了两个方法:
idl
// Gecko 40 有,但 ReactOS 缺失
attribute unsigned long thirdPartyFlags; // 产生 Get + Set 两个 vtable 槽位
这导致 ReactOS 的虚函数表从第 10 项起整体偏移了 2 个位置 。当 Gecko 调用 GetNetworkInterfaceId(vtable 索引 38)时,实际调用的是 ReactOS 的 SetCorsMode,传入的参数被错误地解析为 nsACString*,导致写入无效内存地址 0x00000002,引发 xul.dll 崩溃。
修复
在 nsiface.idl 中添加缺失的两个方法:
idl
nsresult GetThirdPartyFlags(uint32_t *aThirdPartyFlags);
nsresult SetThirdPartyFlags(uint32_t aThirdPartyFlags);
同时在 nsio.c 中实现这两个函数,并在 vtable 中插入对应的条目。修复后 vtable 与 Gecko 40 完全对齐,GetNetworkInterfaceId/SetNetworkInterfaceId 正常工作。
二、已实现的函数清单
nsChannel(nsIHttpChannel 接口)
| 函数 | 实现方式 |
|---|---|
GetContentLength |
返回 This->content_length |
SetContentLength |
存储到 This->content_length |
IsPending |
返回 This->pending 字段 |
Cancel |
设置 This->canceled = TRUE |
Suspend |
no-op,返回 NS_OK |
Resume |
no-op,返回 NS_OK |
GetSecurityInfo |
返回 NULL |
GetContentDisposition |
返回 This->content_disposition |
SetContentDisposition |
存储到 This->content_disposition |
GetContentDispositionFilename |
返回 This->content_disposition_filename(WCHAR* → nsAString) |
SetContentDispositionFilename |
存储 WCHAR*(释放旧值,heap_strdupW 新值) |
VisitRequestHeaders |
调用 visit_http_headers(&This->request_headers, ...) |
GetAllowPipelining |
返回 This->allow_pipelining |
SetAllowPipelining |
存储到 This->allow_pipelining |
GetAllowTLS |
返回 This->allow_tls |
SetAllowTLS |
存储到 This->allow_tls |
GetRedirectionLimit |
返回 This->redirection_limit |
SetRedirectionLimit |
存储到 This->redirection_limit |
SetResponseHeader |
调用 set_channel_http_header(&This->response_headers, ...) |
IsNoStoreResponse |
检查 Cache-Control: no-store(已有实现) |
IsNoCacheResponse |
检查 Cache-Control: no-cache |
IsPrivateResponse |
检查 Cache-Control: private |
RedirectTo |
引用计数存储 nsIURI* 到 This->redirect_to_uri |
GetReferrerPolicy |
返回 This->referrer_policy |
SetReferrerWithPolicy |
存储 referrer URI + policy |
nsIHttpChannelInternal 接口
| 函数 | 实现方式 |
|---|---|
GetDocumentURI |
引用计数返回 This->document_uri |
SetDocumentURI |
引用计数存储 This->document_uri |
GetRequestVersion |
返回 1/1(HTTP/1.1) |
GetResponseVersion |
返回 1/1(HTTP/1.1) |
TakeAllSecurityMessages |
no-op(无安全消息) |
SetCookie |
no-op(cookie 由 wininet 处理) |
SetupFallbackChannel |
no-op |
GetThirdPartyFlags |
返回 This->third_party_flags |
SetThirdPartyFlags |
存储到 This->third_party_flags |
GetForceAllowThirdPartyCookie |
返回 This->force_third_party_cookie |
SetForceAllowThirdPartyCookie |
存储到 This->force_third_party_cookie |
GetCanceled |
返回 This->canceled |
GetChannelIsForDownload |
返回 This->channel_is_for_download |
SetChannelIsForDownload |
存储到 This->channel_is_for_download |
GetLocalAddress |
返回 "0.0.0.0" |
GetLocalPort |
返回 0 |
GetRemoteAddress |
返回 "0.0.0.0" |
GetRemotePort |
返回 0 |
SetCacheKeysRedirectChain |
no-op |
GetAllowSpdy |
返回 This->allow_spdy |
SetAllowSpdy |
存储到 This->allow_spdy |
GetResponseTimeoutEnabled |
返回 This->response_timeout_enabled |
SetResponseTimeoutEnabled |
存储到 This->response_timeout_enabled |
GetApiRedirectToURI |
引用计数返回 This->redirect_to_uri |
GetAllowAltSvc |
返回 This->allow_alt_svc |
SetAllowAltSvc |
存储到 This->allow_alt_svc |
AddRedirect |
no-op |
GetLastModifiedTime |
返回 This->last_modified_time |
ForceNoIntercept |
no-op |
GetCorsIncludeCredentials |
返回 This->cors_include_credentials |
SetCorsIncludeCredentials |
存储到 This->cors_include_credentials |
GetCorsMode |
返回 This->cors_mode |
SetCorsMode |
存储到 This->cors_mode |
GetTopWindowURI |
返回 NULL |
GetNetworkInterfaceId |
返回 This->network_interface_id |
SetNetworkInterfaceId |
存储到 This->network_interface_id |
ContinueBeginConnect |
no-op |
GetProxyURI |
返回 NULL |
保持 NOT_IMPLEMENTED 的函数
| 函数 | 原因 |
|---|---|
nsChannel_Open |
同步打开涉及 urlmon 绑定,实现复杂 |
nsChannel_GetContentDispositionHeader |
需要从 filename/type 构造 Content-Disposition 头 |
HTTPUpgrade |
需要完整的 HTTP Upgrade 协议支持 |
三、结构体字段变更
binding.h --- nsChannel 结构体
新增字段(全部在 /* FIXME function implementation fields */ 区域):
c
INT64 content_length; // Get/SetContentLength
cpp_bool pending; // IsPending
UINT32 content_disposition; // Get/SetContentDisposition
WCHAR *content_disposition_filename; // Get/SetContentDispositionFilename
UINT32 referrer_policy; // GetReferrerPolicy
cpp_bool allow_pipelining; // Get/SetAllowPipelining
cpp_bool allow_tls; // Get/SetAllowTLS
UINT32 redirection_limit; // Get/SetRedirectionLimit
cpp_bool canceled; // GetCanceled
cpp_bool channel_is_for_download; // Get/SetChannelIsForDownload
cpp_bool allow_spdy; // Get/SetAllowSpdy
cpp_bool response_timeout_enabled; // Get/SetResponseTimeoutEnabled
cpp_bool allow_alt_svc; // Get/SetAllowAltSvc
cpp_bool cors_include_credentials; // Get/SetCorsIncludeCredentials
UINT32 cors_mode; // Get/SetCorsMode
UINT32 third_party_flags; // Get/SetThirdPartyFlags
cpp_bool force_third_party_cookie; // Get/SetForceAllowThirdPartyCookie
nsIURI *document_uri; // Get/SetDocumentURI
nsIURI *redirect_to_uri; // RedirectTo / GetApiRedirectToURI
PRTime last_modified_time; // GetLastModifiedTime
char *network_interface_id; // Get/SetNetworkInterfaceId
nsiface.idl --- nsIHttpChannelInternal 接口
新增方法(修复 vtable 对齐):
idl
nsresult GetThirdPartyFlags(uint32_t *aThirdPartyFlags);
nsresult SetThirdPartyFlags(uint32_t aThirdPartyFlags);
四、内存管理
在 nsChannel_Release(引用计数归零时清理)中添加了 heap_free 调用:
c
heap_free(This->content_disposition_filename);
heap_free(This->network_interface_id);
确保堆分配的字符串在通道销毁时被正确释放,避免内存泄漏。
五、经验教训
-
vtable 对齐至关重要 --- ReactOS 与 Gecko 之间的 XPCOM 接口必须完全匹配,即使少一个方法也会导致整个 vtable 偏移,引发难以调试的崩溃。
-
无效指针
0x00000002--- 这个看似可疑的值实际上是 vtable 偏移的证据。当 vtable 错位时,Gecko 调用方法 A 的参数被错误地传递给方法 B,导致参数类型不匹配。 -
NOT_IMPLEMENTED有时更安全 --- 对于某些函数(尤其是 nsIHttpChannelInternal 中的内部 API),返回NS_ERROR_NOT_IMPLEMENTED可能比返回带有默认值的NS_OK更安全,因为前者会阻止 Gecko 进入不完整的代码路径。
六、编译验证
所有修改已通过 ninja mshtml 编译,零错误零警告。
ReactOS 运行测试通过,未出现 xul.dll 崩溃(之前因 vtable 偏移导致的崩溃已消除)。