Cordova应用内嵌谷歌文档查看器iframe报错及崩溃问题
问题场景
在Cordova应用中,通过模态框内嵌iframe加载Google Docs Viewer查看服务器文档,核心代码如下:
$scope.filePath = "https://docs.google.com/gview?url=" + $scope.serverFileURL + "&embedded=true";
<iframe id="pdfFrame" ng-src="{{filePath}}" style="width:100%;height:100%;" frameborder="0"></iframe>
问题1:iframe加载后的后台错误
1.1 脚本执行被CSP阻止的重复错误
后台重复出现两次:
Refused to execute script, violates Content Security Policy directive: "script-src 'report-sample' 'nonce-TPaUqBVHnxjjfUe0rTe09g' 'unsafe-inline' 'strict-dynamic' https: http: 'unsafe-eval'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
1.2 docs.google.com/cspreport 404错误
后台出现两次404:POST https://docs.google.com/cspreport 404,已通过AngularJS的SCE白名单添加了相关域名:
config(function($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist([ 'https://docs.google.com/**', 'https://content.googleapis.com/**', ... ]); })
当前功能正常,但需消除这些错误避免影响后续问题。
问题2:点击弹出按钮后的异常与错误
点击Google Docs Viewer右上角的弹出按钮打开breakout窗口后,触发一系列错误:
GET https://content.googleapis.com/drive/v2internal/about?fields=importFormats,kind&key=AIzaSyDVQw45DwoYh632gvsP5vPDqEKvb-Ywnb8 401GET https://content.googleapis.com/drive/v2internal/apps?fields=items(authorized,chromeExtensionIds,createInFolderTemplate,createUrl,icons(iconUrl,size,category),id,installed,kind,longDescription,name,objectType,openUrlTemplate,origins,primaryFileExtensions,primaryMimeTypes,productId,productUrl,rankingInfo,removable,requiresAuthorizationBeforeOpenWith,secondaryFileExtensions,secondaryMimeTypes,shortDescription,supportsCreate,supportsImport,supportsMultiOpen,supportsTeamDrives,type,useByDefault),kind&languageCode=en-US&key=AIzaSyDVQw45DwoYh632gvsP5vPDqEKvb-Ywnb8 403- 未捕获错误:
"Did not receive drive#about kind when fetching import map:undefined" - 未捕获错误:
API keys are not supported by this API. Expected OAuth2 access token or other authentication credentials
错误导致应用异常:弹出窗口在应用内运行,关闭窗口会直接关闭应用;点击设备返回键出现空白页,最终应用重启崩溃。
解决方案
针对问题1的处理
1.1 CSP脚本执行错误
Google Docs Viewer自身的脚本会触发CSP限制,由于无法修改Google端的CSP配置,可通过以下方式规避:
- 在Cordova的
config.xml中调整Content Security Policy,添加'unsafe-inline'和Google相关域名的脚本允许:
注意:如果应用有严格的安全要求,需评估<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval' https://docs.google.com https://content.googleapis.com; object-src 'self';">'unsafe-inline'的风险,但Google Docs Viewer依赖内联脚本,这是必要的妥协。 - 错误提示中的nonce值由Google生成,无法控制,忽略该提示即可,确保CSP配置覆盖必要来源。
1.2 CSP报告404错误
该错误是Google Docs Viewer尝试向/cspreport提交报告但路径不存在导致的,可通过CSP配置禁止发送报告来避免404:
在CSP中添加report-uri 'none',示例:
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' 'unsafe-eval' https://docs.google.com https://content.googleapis.com; object-src 'self'; report-uri 'none';">
针对问题2的处理
弹出按钮触发Google Drive内部API调用,这些API需要OAuth2授权,应用未提供有效令牌导致认证错误,同时Cordova WebView默认在内部打开新窗口引发异常,解决步骤如下:
拦截弹出窗口,改用系统浏览器打开
使用Cordova的InAppBrowser插件,拦截iframe内的弹出请求,导向系统浏览器:- 安装插件:
cordova plugin add cordova-plugin-inappbrowser - 替换全局
window.open行为或监听iframe弹出事件:document.addEventListener('deviceready', function() { // 替换全局window.open window.open = function(url, target, options) { return cordova.InAppBrowser.open(url, '_system', options); }; // 针对特定iframe监听弹出事件 document.getElementById('pdfFrame').addEventListener('load', function() { var iframeWindow = this.contentWindow; iframeWindow.open = function(url) { cordova.InAppBrowser.open(url, '_system'); return false; // 阻止默认弹出 }; }); }, false);
点击弹出按钮后,系统浏览器会打开文档独立视图,不会影响应用WebView,避免崩溃。
- 安装插件:
禁用Google Docs Viewer的弹出按钮
如果不需要弹出功能,可通过CSS隐藏按钮:/* 加载完成后注入CSS到iframe */ document.getElementById('pdfFrame').addEventListener('load', function() { var style = document.createElement('style'); style.textContent = '.ndfHFb-c4YZDc-Wrql6b { display: none !important; }'; // 需根据实际DOM调整选择器 this.contentDocument.head.appendChild(style); });注意:Google可能调整DOM结构,选择器需定期验证更新。
替换为本地PDF查看方案
如果Google Docs Viewer问题过多,可改用Cordova PDF查看插件,比如cordova-plugin-pdfviewer,避免依赖外部iframe,从根源解决兼容性问题。
内容的提问来源于stack exchange,提问作者rolinger

