XMLHttpRequestでブラウザからローカルファイルにアクセスしようとしたけど無理だった

背景

この記事で,ブラウザから,ローカルにあるテキストファイルを読み込む必要がでたので,XMLHttpRequestを使って実現しようとした.


行ったこと

この記事の「読み込みのみなら」という部分に着目し,manifest.jsonを作ってやってみようと思う.
HTMLファイルと同じ階層に作ればよいらしいのでdocsフォルダに作ることになる.

ファイル構成 (+ manifet.json)

├── ast
├── docs
│   ├── build.wasm
│   ├── index.html
│   ├── manifest.json
│   └── wasm_exec.js
├── evaluator
├── lexer
├── object
├── parser
├── repl
├── server
├── token
├── wasm
│   ├── melody.txt
│   └── webassembly.go
├── go.mod
├── go.sum
└── main.go

ver.を調べる

2022/11/11時点ではver.3が最新なようだ.


実際のコード

{
  "manifest_version": 3,
  "name": "Read File",
  "version": "1.0",

  "host_permissions": ["file:///*"]
}

上から3つのものは必要不可欠なもので,ファイルを読み込めるように拡張しているのはhost_permissionsの部分である.
permissionsを使うと拡張機能のところでエラーとして出されてしまったので次の記事を参考にしてhost_permissionsにした.


あとはこの記事のようにChromeにローカルでインストールすれば良い.



webassembly.go

package main

import (
	"math"
	"syscall/js"
)

var (
	window   = js.Global()
	document = window.Get("document")

	AudioContext   = js.Global().Get("AudioContext")
	OscillatorNode = js.Global().Get("OscillatorNode")
	GainNode       = js.Global().Get("GainNode")
	XMLHttpRequest = js.Global().Get("XMLHttpRequest")

	note2freq = []float64{}
)

func init() {
	for i := 0; i < 128; i++ {
		note2freq = append(note2freq, 440*math.Pow(2, float64(i-69)/12))
	}
}

type Note struct {
	Number   int
	Duration float64
}

type MusicBox struct {
	ctx     js.Value
	osc     js.Value
	gain    js.Value
	current float64
}

func NewMusicBox() *MusicBox {
	ctx := AudioContext.New()
	osc := OscillatorNode.New(ctx)
	gain := GainNode.New(ctx)
	osc.Call("connect", gain)
	osc.Get("frequency").Set("value", 0)
	gain.Call("connect", ctx.Get("destination"))
	gain.Get("gain").Set("value", 0)
	osc.Call("start")
	return &MusicBox{
		ctx:     ctx,
		osc:     osc,
		gain:    gain,
		current: ctx.Get("currentTime").Float(),
	}
}

func (mb *MusicBox) Play(note Note) {
	mb.osc.Get("frequency").Call("setValueAtTime", note2freq[note.Number], mb.current)
	mb.gain.Get("gain").Call("setValueAtTime", 0.3, mb.current)
	mb.gain.Get("gain").Call("setValueAtTime", 0.0, mb.current+note.Duration)
	mb.current += note.Duration
}

const sample = ``

func main() {
	code := document.Call("createElement", "textarea")
	code.Set("id", "code")
	code.Set("value", sample)
	code.Get("style").Set("width", "50%")
	code.Get("style").Set("height", "20em")
	document.Get("body").Call("appendChild", code)
	btn := document.Call("createElement", "button")
	btn.Set("textContent", "music start!")
	btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} {
		//ファイルの読み込み
		xhr := XMLHttpRequest.New()
		xhr.Call("open", "GET", "file:///c:/Users/usrname/Go/01/src/melody.txt")
		xhr.Call("send")
		code.Set("value", xhr.Get("responseText").String())
		

		mb := NewMusicBox()
		window.Set("play", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			mb.Play(Note{args[0].Int(), args[1].Float()})
			return nil
		}))
		code := document.Call("getElementById", "code")
		src := code.Get("value")
		window.Call("eval", src)
		return nil
	}))
	document.Get("body").Call("appendChild", btn)
	select {}
}

実際のコードを上述のようにした.

書き加えた部分は

		xhr := XMLHttpRequest.New()
		xhr.Call("open", "GET", "file:///c:/Users/usrname/Go/01/src/melody.txt")
		xhr.Call("send")
		code.Set("value", xhr.Get("responseText").String())

しかし,このようにしたのちにビルドと実行を行っても

panic: syscall/js: Value.Call: property onload is not a function, got null


調べてみたこと

番号リストリストhttps://qiita.com/smurakami/items/1764a80f40d33dce5243
このような記事が出て,軒並み

open -a /Applications/Google\ Chrome.app --args -allow-file-access-from-files index.html

をコンソールで打ち込めと出てきますが,そういった記事は2012や2013年の記事が多く,不安になってしまいます.
https://taremimi.hatenablog.jp/entry/2018/08/10/170035
新しめのでも2018などですね.
Chromeが開いてある状態でこのコマンドを実行しても意味ないですかね.

teratail上の同様の質問で
https://teratail.com/questions/28389
このようなものがあったのですが,見てみても解決できませんでした.

https://support.apple.com/ja-jp/guide/mac-help/mchld5a35146/mac
このサイトを頼りに「ファイルとフォルダ」を見に行きもしましたが,そもそもChromeはチェックボックスすらなく,Chromeを追加できる様子でもありませんでした.
https://apple.stackexchange.com/questions/385093/how-do-i-grant-access-in-files-and-folders-in-catalina-for-chrome
このサイトではChromeが見えているので何かして追加できるのかもしれません.


結果

Chrome拡張の役割に(ブラウジング体験を向上させるツールの実現など)沿ったなにかを作るのならまだしも、 ブラウザで禁止されていることをしたいためだけにChrome拡張に向かうのは労多く得るものは少ない.
ブラウザで上で実現したいのであればブラウザの機能の中でやりくりするほうが得るものは多い.

JSの非同期挙動(fetch)などで解決することにする.


この記事が気に入ったらサポートをしてみませんか?