文件选取器边栏通信错误

File Picker Sidebar Communication Errors

我正在开发一个独立的脚本,该脚本最终将作为附加组件发布。它将有一个侧边栏用户与之交互,他们可以从那里启动 Google 文件选择器来上传报告以供处理。我以前一直在寻找一种方法让侧边栏知道文件选择器已完成。我成功地复制了 this 答案,并获得了文件选择器对话框,将一条消息发送回侧边栏,表明它已完成。但是,控制台充满了一些错误和警告,我想知道我是否应该关注这些错误和警告。错误是:

Unsafe attempt to initiate navigation for frame with origin 'https://docs.google.com' from frame with URL 'https://***.googleusercontent.com/userCodeAppPanel'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.

DOMException: Blocked a frame with origin "https://###.googleusercontent.com" from accessing a cross-origin frame. at findSideBar (https://###.googleusercontent.com/userCodeAppPanel:80:18) at w0.pickerCallback [as Fb] (https://###.googleusercontent.com/userCodeAppPanel:98:11) at w0._.h.iV (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:740:393) at d (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:215:143) at b (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:210:1)

Refused to get unsafe header "X-HTTP-Status-Code-Override"

我以前见过第一个错误,但是,第二个是新错误,与将消息从文件选择器对话框发送回侧边栏有关。

值得一提的是,一切仍然有效。我的问题主要是这些错误是我应该担心的,在发布前审核附加组件时它们是否会引起问题,我该如何更正它们?

我在下面包含了用于侧边栏创建、选择器创建的代码,以及用于将消息从选择器发送到侧边栏的相关代码。

边栏创建:

function buildSidebar(type) {
  hideColumns();
  hideRows();
  hideSheets();
  if(type == 'setup' || checkSetup()) {
    var html = HtmlService.createTemplateFromFile('SidebarSetupHTML')  
    .evaluate()
    .setTitle('Test');
  } else {
    var html = HtmlService.createTemplateFromFile('SidebarMainHTML')  
    .evaluate()
    .setTitle('Test');    
  }
  SpreadsheetApp.getUi().showSidebar(html);
}

选择器创建:

function showPicker() {
  var html = HtmlService.createHtmlOutputFromFile('PickerHTML.html')
      .setWidth(600)
      .setHeight(425)
      .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  SpreadsheetApp.getUi().showModalDialog(html, 'Select Report(s)');
}

选择器消息代码:

function pickerCallback(data) {
  var action = data[google.picker.Response.ACTION];
  if (action == google.picker.Action.PICKED) {
    (function findSideBar(limit) {
      let f = window.top.frames;
      for (let i = 0; i < limit; ++i) {
        try {
          if (
            f[i] /*/iframedAppPanel*/ &&
            f[i].length &&
            f[i][0] && //#sandboxFrame
            f[i][0][0] && //#userHtmlFrame
            window !== f[i][0][0] //!== self
          ) {
            console.info('Sidebar found');
            var sidebar = f[i][0][0];
            sidebar.modalDone('PICKED'); //Modal has finished
            console.log('Message sent');
            google.script.host.close();
          }
        } catch (e) {
          console.error(e);
          continue;
        }
      }
    })(10);
  }
}

边栏启动选择器并接收消息:

  function testPicker() {
    google.script.run.withSuccessHandler(pickerResponse).showPicker();
  }

  function pickerResponse(e) {
    (async () => {      
      let receiver = new Promise((res, rej) => {
        window.modalDone = res;
      });
      var message = await receiver;
      console.log('message received');
      if(message == 'PICKED' || message == "NOT_PICKED") {
        console.log(message);
      }
      //Do what you want here
    })();
  }

当我看到你的脚本时,我认为你的问题的原因可能是 if 语句 true 之后的循环。因此,例如,当 if 语句是 true 时,将 break 放在最后一行怎么样?那么,下面的修改呢?

发件人:

if (
  f[i] /*/iframedAppPanel*/ &&
  f[i].length &&
  f[i][0] && //#sandboxFrame
  f[i][0][0] && //#userHtmlFrame
  window !== f[i][0][0] //!== self
) {
  console.info('Sidebar found');
  var sidebar = f[i][0][0];
  sidebar.modalDone('PICKED'); //Modal has finished
  console.log('Message sent');
  google.script.host.close();
}

收件人:

if (
  f[i] /*/iframedAppPanel*/ &&
  f[i].length &&
  f[i][0] && //#sandboxFrame
  f[i][0][0] && //#userHtmlFrame
  window !== f[i][0][0] //!== self
) {
  console.info('Sidebar found');
  var sidebar = f[i][0][0];
  sidebar.modalDone('PICKED'); //Modal has finished
  console.log('Message sent');
  google.script.host.close();
  break; // <--- Added
}

注:

当我在 Google 文档(例如 Google 电子表格)上测试自定义对话框的以下示例脚本时,

<script>
  google.script.host.close();
  alert("ok");
</script>

我确认警报 window 已打开,并且对话框已关闭。所以我认为 google.script.host.close() 可能适用于异步进程。

例如,当google.script.run的处理成本较高时,如下使用,

<script>
  google.script.host.close();
  google.script.run.withSuccessHandler(_ => alert("ok2")).myFunction();
  alert("ok1");
</script>

似乎 myFunction()alert("ok2") 不是 运行 因为对话框在 运行 myFunction() 之前关闭,而 alert("ok1") 是运行。另一方面,当进程成本低时,google.script.host.close()之后的脚本似乎是运行。

鉴于这种情况,作为尝试,我提议在OP的问题google.script.host.close()之后添加break

参考: