├── .gitattributes ├── .github └── workflows │ └── build_gcd_v2.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── chrome_target.go ├── examples ├── events │ ├── events.go │ └── webdata │ │ ├── iframe.html │ │ ├── target.html │ │ └── top.html ├── getdoc │ └── getdoc.go ├── loadpage │ └── loadpage.go └── screenshot │ └── screenshot.go ├── gcd.go ├── gcd_test.go ├── gcdapi ├── accessibility.go ├── animation.go ├── applicationcache.go ├── audits.go ├── backgroundservice.go ├── browser.go ├── cachestorage.go ├── cast.go ├── console.go ├── css.go ├── database.go ├── debugger.go ├── deviceorientation.go ├── dom.go ├── domdebugger.go ├── domsnapshot.go ├── domstorage.go ├── emulation.go ├── fetch.go ├── headlessexperimental.go ├── heapprofiler.go ├── indexeddb.go ├── input.go ├── inspector.go ├── io.go ├── layertree.go ├── log.go ├── media.go ├── memory.go ├── network.go ├── overlay.go ├── page.go ├── performance.go ├── profiler.go ├── runtime.go ├── schema.go ├── security.go ├── serviceworker.go ├── storage.go ├── systeminfo.go ├── target.go ├── tethering.go ├── tracing.go ├── version.go ├── webaudio.go └── webauthn.go ├── gcdapigen ├── README.md ├── api_template.txt ├── build.sh ├── command.go ├── domain.go ├── downloader.go ├── event.go ├── gcdapigen.go ├── protocol.go ├── protocol.json ├── return.go ├── template_funcs.go ├── type.go ├── type_properties.go └── utils.go ├── gcdmessage └── gcdmessage.go ├── go.mod ├── go.sum ├── testdata ├── console_log.html ├── cookie.html └── extension │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── _locales │ ├── en │ │ └── messages.json │ └── en_GB │ │ └── messages.json │ ├── background.js │ ├── images │ ├── icon128.png │ ├── icon38-off.png │ ├── icon38-on.png │ └── icon48.png │ └── manifest.json ├── v2 ├── chrome_target.go ├── examples │ ├── events │ │ ├── events.go │ │ └── webdata │ │ │ ├── iframe.html │ │ │ ├── target.html │ │ │ └── top.html │ ├── getdoc │ │ └── getdoc.go │ ├── loadpage │ │ └── loadpage.go │ └── screenshot │ │ └── screenshot.go ├── gcd.go ├── gcd_test.go ├── gcdapi │ ├── accessibility.go │ ├── animation.go │ ├── audits.go │ ├── autofill.go │ ├── backgroundservice.go │ ├── bluetoothemulation.go │ ├── browser.go │ ├── cachestorage.go │ ├── cast.go │ ├── console.go │ ├── css.go │ ├── database.go │ ├── debugger.go │ ├── deviceaccess.go │ ├── deviceorientation.go │ ├── dom.go │ ├── domdebugger.go │ ├── domsnapshot.go │ ├── domstorage.go │ ├── emulation.go │ ├── eventbreakpoints.go │ ├── extensions.go │ ├── fedcm.go │ ├── fetch.go │ ├── filesystem.go │ ├── headlessexperimental.go │ ├── heapprofiler.go │ ├── indexeddb.go │ ├── input.go │ ├── inspector.go │ ├── io.go │ ├── layertree.go │ ├── log.go │ ├── media.go │ ├── memory.go │ ├── network.go │ ├── overlay.go │ ├── page.go │ ├── performance.go │ ├── performancetimeline.go │ ├── preload.go │ ├── profiler.go │ ├── pwa.go │ ├── runtime.go │ ├── schema.go │ ├── security.go │ ├── serviceworker.go │ ├── storage.go │ ├── systeminfo.go │ ├── target.go │ ├── tethering.go │ ├── tracing.go │ ├── version.go │ ├── webaudio.go │ └── webauthn.go ├── gcdapigen │ ├── README.md │ ├── api_template.txt │ ├── build.sh │ ├── command.go │ ├── domain.go │ ├── downloader.go │ ├── event.go │ ├── gcdapigen.go │ ├── protocol.go │ ├── protocol.json │ ├── return.go │ ├── template_funcs.go │ ├── type.go │ ├── type_properties.go │ └── utils.go ├── gcdmessage │ └── gcdmessage.go ├── go.mod ├── go.sum ├── logger.go ├── observer │ └── message_observer.go ├── testdata │ ├── console_log.html │ ├── cookie.html │ └── extension │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── _locales │ │ ├── en │ │ │ └── messages.json │ │ └── en_GB │ │ │ └── messages.json │ │ ├── background.js │ │ ├── images │ │ ├── icon128.png │ │ ├── icon38-off.png │ │ ├── icon38-on.png │ │ └── icon48.png │ │ └── manifest.json └── wsconn.go └── wsconn.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.github/workflows/build_gcd_v2.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Install Go 8 | uses: actions/setup-go@v2 9 | with: 10 | go-version: 1.19.x 11 | - name: Install Packages 12 | run: | 13 | sudo apt-get -qq update 14 | sudo apt-get install -y build-essential curl 15 | echo -n "Determining latest build... " 16 | LATEST=`curl -s http://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/LAST_CHANGE` 17 | echo "done!" 18 | echo "http://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/$LATEST/chrome-linux.zip" 19 | echo -n "Downloading build for $LATEST... " 20 | curl -L http://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/$LATEST/chrome-linux.zip -o chrome-linux.zip 21 | echo "done!" 22 | - name: Checkout code 23 | uses: actions/checkout@v2 24 | - name: Test 25 | run: | 26 | cd v2 && go test -v -race -p 1 ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must ends with two \r. 29 | Icon 30 | 31 | 32 | # Thumbnails 33 | ._* 34 | 35 | # Files that might appear on external disk 36 | .Spotlight-V100 37 | .Trashes 38 | 39 | *.exe 40 | debug.log 41 | 42 | output 43 | 44 | gcdapigen/gcdapigen 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 isaac dawson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Report Card](https://goreportcard.com/badge/github.com/wirepair/gcd)](https://goreportcard.com/report/github.com/wirepair/gcd) 2 | 3 | # Google Chrome Debugger (GCD) 4 | 5 | This is primarly an auto-generated client library for communicating with a Google Chrome Browser over their [remote client debugger protocol](https://developer.chrome.com/devtools/docs/debugger-protocol). Note that their documentation is partially incorrect and does not contain a lot of the API calls that are actually available. 6 | 7 | Because I'm lazy and there are hundereds of different custom types and API methods, this library has been automatically generated using their [protocol.json](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/devtools/protocol.json&q=protocol.json&sq=package:chromium&type=cs). 8 | 9 | The [gcdapigen](https://github.com/wirepair/gcd/tree/master/v2/gcdapigen) program was created to generate types, event types and commands for gcd. 10 | 11 | # Changelog 12 | 13 | See the [CHANGELOG](https://github.com/wirepair/gcd/tree/master/CHANGELOG.md). 14 | 15 | ## Dependencies 16 | 17 | gcd requires the [gcdapi](https://github.com/wirepair/gcd/tree/master/v2/gcdapi) and [gcdmessage](https://github.com/wirepair/gcd/tree/master/v2/gcdmessage) packages. gcdapi is the auto-generated API. gcdmessage is the glue between gcd and gcdapi so we can keep the packages clean. 18 | 19 | ## The API 20 | 21 | The API consists of of synchronous requests, asynchronous requests / events. Synchronous requests are handled by using non-buffered channels and methods can be called and will return once the value is available. Events are handled by subscribing the response method type and calling the API's "Enable()" such as: 22 | 23 | ```Go 24 | target, err := debugger.NewTab() 25 | if err != nil { 26 | log.Fatalf("error getting new tab: %s\n", err) 27 | } 28 | console := target.Console 29 | 30 | target.Subscribe("Console.messageAdded", func(target *ChromeTarget, v []byte) { 31 | 32 | msg := &gcdapi.ConsoleMessageAddedEvent{} 33 | err := json.Unmarshal(v, msg) 34 | if err != nil { 35 | log.Fatalf("error unmarshalling event data: %v\n", err) 36 | } 37 | log.Printf("METHOD: %s\n", msg.Method) 38 | eventData := msg.Params.Message 39 | log.Printf("Got event: %s\n", eventData) 40 | }) 41 | console.Enable() 42 | // recv events 43 | // console.Disable() 44 | ``` 45 | 46 | ## Usage 47 | 48 | For a full list of api methods, types, event types & godocs: [Documentation](https://godoc.org/github.com/wirepair/gcd/v2/gcdapi) 49 | 50 | ## Examples 51 | 52 | See [examples](https://github.com/wirepair/gcd/tree/master/v2/examples) 53 | 54 | ## Licensing 55 | 56 | The MIT License (MIT) 57 | 58 | Copyright (c) 2020 isaac dawson 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in 68 | all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | THE SOFTWARE. 77 | -------------------------------------------------------------------------------- /examples/events/events.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example of using DebugEvents(true) to listen to various events being emitted by 3 | the Remote Chrome Debugger 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "encoding/json" 10 | "flag" 11 | "fmt" 12 | "io/ioutil" 13 | "log" 14 | "net" 15 | "net/http" 16 | "os" 17 | "time" 18 | 19 | "github.com/wirepair/gcd" 20 | "github.com/wirepair/gcd/gcdapi" 21 | ) 22 | 23 | var ( 24 | testListener net.Listener 25 | testPath string 26 | testDir string 27 | testPort string 28 | testServerAddr string 29 | ) 30 | 31 | var testStartupFlags = []string{"--disable-new-tab-first-run", "--no-first-run", "--disable-popup-blocking"} 32 | 33 | func init() { 34 | flag.StringVar(&testPath, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 35 | flag.StringVar(&testDir, "dir", "C:\\temp\\", "user directory") 36 | flag.StringVar(&testPort, "port", "9222", "Debugger port") 37 | } 38 | 39 | func main() { 40 | navigatedCh := make(chan struct{}) 41 | debugger := startGcd() 42 | defer debugger.ExitProcess() 43 | 44 | target := startTarget(debugger) 45 | 46 | // subscribe to page loaded event 47 | target.Subscribe("Page.loadEventFired", func(targ *gcd.ChromeTarget, payload []byte) { 48 | navigatedCh <- struct{}{} 49 | }) 50 | 51 | // navigate 52 | navigateParams := &gcdapi.PageNavigateParams{Url: testServerAddr + "top.html"} 53 | _, _, _, err := target.Page.NavigateWithParams(navigateParams) 54 | if err != nil { 55 | log.Fatalf("error: %s\n", err) 56 | } 57 | 58 | // wait for navigation to finish 59 | <-navigatedCh 60 | // get the document node 61 | doc, err := target.DOM.GetDocument(-1, true) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | // request child nodes, this will come in as DOM.setChildNode events 67 | for i := 0; i < 3; i++ { 68 | log.Printf("requesting child nodes...") 69 | _, err = target.DOM.RequestChildNodes(doc.NodeId, -1, true) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | time.Sleep(1 * time.Second) 74 | } 75 | 76 | target.Subscribe("DOM.setChildNodes", func(targ *gcd.ChromeTarget, payload []byte) { 77 | setNodes := &gcdapi.DOMSetChildNodesEvent{} 78 | err := json.Unmarshal(payload, setNodes) 79 | if err != nil { 80 | log.Fatalf("error decoding setChildNodes") 81 | } 82 | for _, x := range setNodes.Params.Nodes { 83 | if x.ContentDocument != nil { 84 | checkContentDocument(targ, x) 85 | } 86 | for _, v := range x.Children { 87 | if v.ContentDocument != nil { 88 | checkContentDocument(targ, v) 89 | } 90 | } 91 | } 92 | 93 | }) 94 | 95 | // wait for redirect 96 | //time.Sleep(5 * time.Second) 97 | 98 | // get iframe node id 99 | iframe, err := target.DOM.QuerySelector(doc.NodeId, "#iframe") 100 | if err != nil { 101 | log.Fatalf("error looking for frame") 102 | } 103 | 104 | log.Printf("iframe %d\n", iframe) 105 | time.Sleep(10 * time.Second) 106 | debugger.ExitProcess() 107 | os.RemoveAll(testDir) // remove new profile junk 108 | } 109 | 110 | func checkContentDocument(targ *gcd.ChromeTarget, v *gcdapi.DOMNode) { 111 | if v.ContentDocument != nil { 112 | iframeDocId := v.ContentDocument.NodeId 113 | targ.DOM.RequestChildNodes(iframeDocId, -1, true) 114 | //nodes, _ := targ.DOM.QuerySelectorAll(iframeDocId, "div") 115 | log.Printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!got iframe nodes.\n") 116 | } else { 117 | log.Printf("got non iframe\n") 118 | } 119 | } 120 | 121 | func startGcd() *gcd.Gcd { 122 | testDir = testRandomDir() 123 | testPort = testRandomPort() 124 | testServer() // start test web server 125 | debugger := gcd.NewChromeDebugger() 126 | debugger.AddFlags(testStartupFlags) 127 | debugger.StartProcess(testPath, testDir, testPort) 128 | return debugger 129 | } 130 | 131 | func startTarget(debugger *gcd.Gcd) *gcd.ChromeTarget { 132 | target, err := debugger.NewTab() 133 | if err != nil { 134 | log.Fatalf("error getting new tab: %s\n", err) 135 | } 136 | target.DebugEvents(true) 137 | target.DOM.Enable() 138 | target.Console.Enable() 139 | target.Page.Enable() 140 | //target.Debugger.Enable() 141 | return target 142 | 143 | } 144 | 145 | func testServer() { 146 | testListener, _ = net.Listen("tcp", ":0") 147 | _, testServerPort, _ := net.SplitHostPort(testListener.Addr().String()) 148 | testServerAddr = fmt.Sprintf("http://localhost:%s/", testServerPort) 149 | go http.Serve(testListener, http.FileServer(http.Dir("webdata/"))) 150 | } 151 | 152 | func testRandomPort() string { 153 | l, err := net.Listen("tcp", ":0") 154 | if err != nil { 155 | log.Fatal(err) 156 | } 157 | _, randPort, _ := net.SplitHostPort(l.Addr().String()) 158 | l.Close() 159 | return randPort 160 | } 161 | 162 | func testRandomDir() string { 163 | dir, err := ioutil.TempDir(testDir, "autogcd") 164 | if err != nil { 165 | log.Fatalf("error getting temp dir: %s\n", err) 166 | } 167 | return dir 168 | } 169 | -------------------------------------------------------------------------------- /examples/events/webdata/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | document modification 4 | 20 | 21 | 22 |
23 |
see ya!
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/events/webdata/target.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | target of redirect 4 | 27 | 28 | 29 |
30 |
i'm a target page
31 |
i'm a target page
32 |
i'm a target page
33 |
i'm a target page
34 |
i'm a target page
35 |
i'm a target page
36 |
i'm a target page
37 |
i'm a target page
38 |
i'm a target page
39 |
i'm a target page
40 |
i'm a target page
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/events/webdata/top.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | frame top 4 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/getdoc/getdoc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/wirepair/gcd" 6 | "log" 7 | "runtime" 8 | ) 9 | 10 | var path string 11 | var dir string 12 | var port string 13 | 14 | func init() { 15 | switch runtime.GOOS { 16 | case "windows": 17 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 18 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 19 | case "darwin": 20 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 21 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 22 | case "linux": 23 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 24 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 25 | } 26 | 27 | flag.StringVar(&port, "port", "9222", "Debugger port") 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | debugger := gcd.NewChromeDebugger() 33 | debugger.StartProcess(path, dir, port) 34 | defer debugger.ExitProcess() 35 | target, err := debugger.NewTab() 36 | if err != nil { 37 | log.Fatalf("error getting new tab: %s\n", err) 38 | } 39 | dom := target.DOM 40 | r, err := dom.GetDocument(-1, true) 41 | if err != nil { 42 | log.Fatalf("error: %s\n", err) 43 | } 44 | 45 | log.Printf("NodeID: %d Node Name: %s, URL: %s\n", r.NodeId, r.NodeName, r.DocumentURL) 46 | } 47 | -------------------------------------------------------------------------------- /examples/loadpage/loadpage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "runtime" 7 | "sync" 8 | 9 | "github.com/wirepair/gcd" 10 | "github.com/wirepair/gcd/gcdapi" 11 | ) 12 | 13 | var path string 14 | var dir string 15 | var port string 16 | 17 | func init() { 18 | switch runtime.GOOS { 19 | case "windows": 20 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 21 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 22 | case "darwin": 23 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 24 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 25 | case "linux": 26 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 27 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 28 | } 29 | 30 | flag.StringVar(&port, "port", "9222", "Debugger port") 31 | } 32 | 33 | func main() { 34 | var wg sync.WaitGroup 35 | wg.Add(1) 36 | flag.Parse() 37 | debugger := gcd.NewChromeDebugger() 38 | 39 | // start process, specify a tmp profile path so we get a fresh profiled browser 40 | // set port 9222 as the debug port 41 | debugger.StartProcess(path, dir, port) 42 | defer debugger.ExitProcess() // exit when done 43 | 44 | target, err := debugger.NewTab() 45 | if err != nil { 46 | log.Fatalf("error opening new tab: %s\n", err) 47 | } 48 | 49 | //subscribe to page load 50 | target.Subscribe("Page.loadEventFired", func(targ *gcd.ChromeTarget, v []byte) { 51 | doc, err := target.DOM.GetDocument(-1, true) 52 | if err == nil { 53 | log.Printf("%s\n", doc.DocumentURL) 54 | } 55 | wg.Done() // page loaded, we can exit now 56 | // if you wanted to inspect the full response data, you could do that here 57 | }) 58 | 59 | // get the Page API and enable it 60 | if _, err := target.Page.Enable(); err != nil { 61 | log.Fatalf("error getting page: %s\n", err) 62 | } 63 | 64 | navigateParams := &gcdapi.PageNavigateParams{Url: "http://www.veracode.com"} 65 | ret, _, _, err := target.Page.NavigateWithParams(navigateParams) // navigate 66 | if err != nil { 67 | log.Fatalf("Error navigating: %s\n", err) 68 | } 69 | 70 | log.Printf("ret: %#v\n", ret) 71 | wg.Wait() // wait for page load 72 | debugger.CloseTab(target) 73 | } 74 | -------------------------------------------------------------------------------- /examples/screenshot/screenshot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net/url" 9 | "os" 10 | "runtime" 11 | "sync" 12 | "time" 13 | 14 | "github.com/wirepair/gcd" 15 | "github.com/wirepair/gcd/gcdapi" 16 | ) 17 | 18 | const ( 19 | numTabs = 5 20 | ) 21 | 22 | var debugger *gcd.Gcd 23 | var wg sync.WaitGroup 24 | 25 | var path string 26 | var dir string 27 | var port string 28 | 29 | func init() { 30 | switch runtime.GOOS { 31 | case "windows": 32 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 33 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 34 | case "darwin": 35 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 36 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 37 | case "linux": 38 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 39 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 40 | } 41 | 42 | flag.StringVar(&port, "port", "9222", "Debugger port") 43 | } 44 | 45 | func main() { 46 | var err error 47 | urls := []string{"http://www.google.com", "http://www.veracode.com", "http://www.microsoft.com", "http://bbc.co.uk", "http://www.reddit.com/r/golang"} 48 | 49 | flag.Parse() 50 | 51 | debugger = gcd.NewChromeDebugger() 52 | debugger.StartProcess(path, dir, port) 53 | defer debugger.ExitProcess() 54 | 55 | targets := make([]*gcd.ChromeTarget, numTabs) 56 | 57 | for i := 0; i < numTabs; i++ { 58 | wg.Add(1) 59 | targets[i], err = debugger.NewTab() 60 | if err != nil { 61 | log.Fatalf("error getting targets") 62 | } 63 | page := targets[i].Page 64 | page.Enable() 65 | targets[i].Subscribe("Page.loadEventFired", pageLoaded) 66 | // navigate 67 | navigateParams := &gcdapi.PageNavigateParams{Url: urls[i]} 68 | _, _, _, err := page.NavigateWithParams(navigateParams) 69 | if err != nil { 70 | log.Fatalf("error: %s\n", err) 71 | } 72 | } 73 | wg.Wait() 74 | for i := 0; i < numTabs; i++ { 75 | takeScreenShot(targets[i]) 76 | } 77 | } 78 | 79 | func pageLoaded(target *gcd.ChromeTarget, event []byte) { 80 | target.Unsubscribe("Page.loadEventFired") 81 | wg.Done() 82 | } 83 | 84 | func takeScreenShot(target *gcd.ChromeTarget) { 85 | dom := target.DOM 86 | page := target.Page 87 | doc, err := dom.GetDocument(-1, true) 88 | if err != nil { 89 | fmt.Printf("error getting doc: %s\n", err) 90 | return 91 | } 92 | 93 | debugger.ActivateTab(target) 94 | time.Sleep(1 * time.Second) // give it a sec to paint 95 | u, urlErr := url.Parse(doc.DocumentURL) 96 | if urlErr != nil { 97 | fmt.Printf("error parsing url: %s\n", urlErr) 98 | return 99 | } 100 | 101 | fmt.Printf("Taking screen shot of: %s\n", u.Host) 102 | screenShotParams := &gcdapi.PageCaptureScreenshotParams{Format: "png", FromSurface: true} 103 | img, errCap := page.CaptureScreenshotWithParams(screenShotParams) 104 | if errCap != nil { 105 | fmt.Printf("error getting doc: %s\n", errCap) 106 | return 107 | } 108 | 109 | imgBytes, errDecode := base64.StdEncoding.DecodeString(img) 110 | if errDecode != nil { 111 | fmt.Printf("error decoding image: %s\n", errDecode) 112 | return 113 | } 114 | 115 | f, errFile := os.Create(u.Host + ".png") 116 | defer f.Close() 117 | if errFile != nil { 118 | fmt.Printf("error creating image file: %s\n", errFile) 119 | return 120 | } 121 | f.Write(imgBytes) 122 | debugger.CloseTab(target) 123 | } 124 | -------------------------------------------------------------------------------- /gcdapi/cast.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Cast functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // No Description. 12 | type CastSink struct { 13 | Name string `json:"name"` // 14 | Id string `json:"id"` // 15 | Session string `json:"session,omitempty"` // Text describing the current session. Present only if there is an active session on the sink. 16 | } 17 | 18 | // This is fired whenever the list of available sinks changes. A sink is a device or a software surface that you can cast to. 19 | type CastSinksUpdatedEvent struct { 20 | Method string `json:"method"` 21 | Params struct { 22 | Sinks []*CastSink `json:"sinks"` // 23 | } `json:"Params,omitempty"` 24 | } 25 | 26 | // This is fired whenever the outstanding issue/error message changes. |issueMessage| is empty if there is no issue. 27 | type CastIssueUpdatedEvent struct { 28 | Method string `json:"method"` 29 | Params struct { 30 | IssueMessage string `json:"issueMessage"` // 31 | } `json:"Params,omitempty"` 32 | } 33 | 34 | type Cast struct { 35 | target gcdmessage.ChromeTargeter 36 | } 37 | 38 | func NewCast(target gcdmessage.ChromeTargeter) *Cast { 39 | c := &Cast{target: target} 40 | return c 41 | } 42 | 43 | type CastEnableParams struct { 44 | // 45 | PresentationUrl string `json:"presentationUrl,omitempty"` 46 | } 47 | 48 | // EnableWithParams - Starts observing for sinks that can be used for tab mirroring, and if set, sinks compatible with |presentationUrl| as well. When sinks are found, a |sinksUpdated| event is fired. Also starts observing for issue messages. When an issue is added or removed, an |issueUpdated| event is fired. 49 | func (c *Cast) EnableWithParams(v *CastEnableParams) (*gcdmessage.ChromeResponse, error) { 50 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.enable", Params: v}) 51 | } 52 | 53 | // Enable - Starts observing for sinks that can be used for tab mirroring, and if set, sinks compatible with |presentationUrl| as well. When sinks are found, a |sinksUpdated| event is fired. Also starts observing for issue messages. When an issue is added or removed, an |issueUpdated| event is fired. 54 | // presentationUrl - 55 | func (c *Cast) Enable(presentationUrl string) (*gcdmessage.ChromeResponse, error) { 56 | var v CastEnableParams 57 | v.PresentationUrl = presentationUrl 58 | return c.EnableWithParams(&v) 59 | } 60 | 61 | // Stops observing for sinks and issues. 62 | func (c *Cast) Disable() (*gcdmessage.ChromeResponse, error) { 63 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.disable"}) 64 | } 65 | 66 | type CastSetSinkToUseParams struct { 67 | // 68 | SinkName string `json:"sinkName"` 69 | } 70 | 71 | // SetSinkToUseWithParams - Sets a sink to be used when the web page requests the browser to choose a sink via Presentation API, Remote Playback API, or Cast SDK. 72 | func (c *Cast) SetSinkToUseWithParams(v *CastSetSinkToUseParams) (*gcdmessage.ChromeResponse, error) { 73 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.setSinkToUse", Params: v}) 74 | } 75 | 76 | // SetSinkToUse - Sets a sink to be used when the web page requests the browser to choose a sink via Presentation API, Remote Playback API, or Cast SDK. 77 | // sinkName - 78 | func (c *Cast) SetSinkToUse(sinkName string) (*gcdmessage.ChromeResponse, error) { 79 | var v CastSetSinkToUseParams 80 | v.SinkName = sinkName 81 | return c.SetSinkToUseWithParams(&v) 82 | } 83 | 84 | type CastStartTabMirroringParams struct { 85 | // 86 | SinkName string `json:"sinkName"` 87 | } 88 | 89 | // StartTabMirroringWithParams - Starts mirroring the tab to the sink. 90 | func (c *Cast) StartTabMirroringWithParams(v *CastStartTabMirroringParams) (*gcdmessage.ChromeResponse, error) { 91 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.startTabMirroring", Params: v}) 92 | } 93 | 94 | // StartTabMirroring - Starts mirroring the tab to the sink. 95 | // sinkName - 96 | func (c *Cast) StartTabMirroring(sinkName string) (*gcdmessage.ChromeResponse, error) { 97 | var v CastStartTabMirroringParams 98 | v.SinkName = sinkName 99 | return c.StartTabMirroringWithParams(&v) 100 | } 101 | 102 | type CastStopCastingParams struct { 103 | // 104 | SinkName string `json:"sinkName"` 105 | } 106 | 107 | // StopCastingWithParams - Stops the active Cast session on the sink. 108 | func (c *Cast) StopCastingWithParams(v *CastStopCastingParams) (*gcdmessage.ChromeResponse, error) { 109 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.stopCasting", Params: v}) 110 | } 111 | 112 | // StopCasting - Stops the active Cast session on the sink. 113 | // sinkName - 114 | func (c *Cast) StopCasting(sinkName string) (*gcdmessage.ChromeResponse, error) { 115 | var v CastStopCastingParams 116 | v.SinkName = sinkName 117 | return c.StopCastingWithParams(&v) 118 | } 119 | -------------------------------------------------------------------------------- /gcdapi/console.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Console functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Console message. 12 | type ConsoleConsoleMessage struct { 13 | Source string `json:"source"` // Message source. 14 | Level string `json:"level"` // Message severity. 15 | Text string `json:"text"` // Message text. 16 | Url string `json:"url,omitempty"` // URL of the message origin. 17 | Line int `json:"line,omitempty"` // Line number in the resource that generated this message (1-based). 18 | Column int `json:"column,omitempty"` // Column number in the resource that generated this message (1-based). 19 | } 20 | 21 | // Issued when new console message is added. 22 | type ConsoleMessageAddedEvent struct { 23 | Method string `json:"method"` 24 | Params struct { 25 | Message *ConsoleConsoleMessage `json:"message"` // Console message that has been added. 26 | } `json:"Params,omitempty"` 27 | } 28 | 29 | type Console struct { 30 | target gcdmessage.ChromeTargeter 31 | } 32 | 33 | func NewConsole(target gcdmessage.ChromeTargeter) *Console { 34 | c := &Console{target: target} 35 | return c 36 | } 37 | 38 | // Does nothing. 39 | func (c *Console) ClearMessages() (*gcdmessage.ChromeResponse, error) { 40 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.clearMessages"}) 41 | } 42 | 43 | // Disables console domain, prevents further console messages from being reported to the client. 44 | func (c *Console) Disable() (*gcdmessage.ChromeResponse, error) { 45 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.disable"}) 46 | } 47 | 48 | // Enables console domain, sends the messages collected so far to the client by means of the `messageAdded` notification. 49 | func (c *Console) Enable() (*gcdmessage.ChromeResponse, error) { 50 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.enable"}) 51 | } 52 | -------------------------------------------------------------------------------- /gcdapi/database.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Database functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Database object. 12 | type DatabaseDatabase struct { 13 | Id string `json:"id"` // Database ID. 14 | Domain string `json:"domain"` // Database domain. 15 | Name string `json:"name"` // Database name. 16 | Version string `json:"version"` // Database version. 17 | } 18 | 19 | // Database error. 20 | type DatabaseError struct { 21 | Message string `json:"message"` // Error message. 22 | Code int `json:"code"` // Error code. 23 | } 24 | 25 | // 26 | type DatabaseAddDatabaseEvent struct { 27 | Method string `json:"method"` 28 | Params struct { 29 | Database *DatabaseDatabase `json:"database"` // 30 | } `json:"Params,omitempty"` 31 | } 32 | 33 | type Database struct { 34 | target gcdmessage.ChromeTargeter 35 | } 36 | 37 | func NewDatabase(target gcdmessage.ChromeTargeter) *Database { 38 | c := &Database{target: target} 39 | return c 40 | } 41 | 42 | // Disables database tracking, prevents database events from being sent to the client. 43 | func (c *Database) Disable() (*gcdmessage.ChromeResponse, error) { 44 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.disable"}) 45 | } 46 | 47 | // Enables database tracking, database events will now be delivered to the client. 48 | func (c *Database) Enable() (*gcdmessage.ChromeResponse, error) { 49 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.enable"}) 50 | } 51 | 52 | type DatabaseExecuteSQLParams struct { 53 | // 54 | DatabaseId string `json:"databaseId"` 55 | // 56 | Query string `json:"query"` 57 | } 58 | 59 | // ExecuteSQLWithParams - 60 | // Returns - columnNames - values - sqlError - 61 | func (c *Database) ExecuteSQLWithParams(v *DatabaseExecuteSQLParams) ([]string, []interface{}, *DatabaseError, error) { 62 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.executeSQL", Params: v}) 63 | if err != nil { 64 | return nil, nil, nil, err 65 | } 66 | 67 | var chromeData struct { 68 | Result struct { 69 | ColumnNames []string 70 | Values []interface{} 71 | SqlError *DatabaseError 72 | } 73 | } 74 | 75 | if resp == nil { 76 | return nil, nil, nil, &gcdmessage.ChromeEmptyResponseErr{} 77 | } 78 | 79 | // test if error first 80 | cerr := &gcdmessage.ChromeErrorResponse{} 81 | json.Unmarshal(resp.Data, cerr) 82 | if cerr != nil && cerr.Error != nil { 83 | return nil, nil, nil, &gcdmessage.ChromeRequestErr{Resp: cerr} 84 | } 85 | 86 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 87 | return nil, nil, nil, err 88 | } 89 | 90 | return chromeData.Result.ColumnNames, chromeData.Result.Values, chromeData.Result.SqlError, nil 91 | } 92 | 93 | // ExecuteSQL - 94 | // databaseId - 95 | // query - 96 | // Returns - columnNames - values - sqlError - 97 | func (c *Database) ExecuteSQL(databaseId string, query string) ([]string, []interface{}, *DatabaseError, error) { 98 | var v DatabaseExecuteSQLParams 99 | v.DatabaseId = databaseId 100 | v.Query = query 101 | return c.ExecuteSQLWithParams(&v) 102 | } 103 | 104 | type DatabaseGetDatabaseTableNamesParams struct { 105 | // 106 | DatabaseId string `json:"databaseId"` 107 | } 108 | 109 | // GetDatabaseTableNamesWithParams - 110 | // Returns - tableNames - 111 | func (c *Database) GetDatabaseTableNamesWithParams(v *DatabaseGetDatabaseTableNamesParams) ([]string, error) { 112 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.getDatabaseTableNames", Params: v}) 113 | if err != nil { 114 | return nil, err 115 | } 116 | 117 | var chromeData struct { 118 | Result struct { 119 | TableNames []string 120 | } 121 | } 122 | 123 | if resp == nil { 124 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 125 | } 126 | 127 | // test if error first 128 | cerr := &gcdmessage.ChromeErrorResponse{} 129 | json.Unmarshal(resp.Data, cerr) 130 | if cerr != nil && cerr.Error != nil { 131 | return nil, &gcdmessage.ChromeRequestErr{Resp: cerr} 132 | } 133 | 134 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 135 | return nil, err 136 | } 137 | 138 | return chromeData.Result.TableNames, nil 139 | } 140 | 141 | // GetDatabaseTableNames - 142 | // databaseId - 143 | // Returns - tableNames - 144 | func (c *Database) GetDatabaseTableNames(databaseId string) ([]string, error) { 145 | var v DatabaseGetDatabaseTableNamesParams 146 | v.DatabaseId = databaseId 147 | return c.GetDatabaseTableNamesWithParams(&v) 148 | } 149 | -------------------------------------------------------------------------------- /gcdapi/deviceorientation.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains DeviceOrientation functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | type DeviceOrientation struct { 12 | target gcdmessage.ChromeTargeter 13 | } 14 | 15 | func NewDeviceOrientation(target gcdmessage.ChromeTargeter) *DeviceOrientation { 16 | c := &DeviceOrientation{target: target} 17 | return c 18 | } 19 | 20 | // Clears the overridden Device Orientation. 21 | func (c *DeviceOrientation) ClearDeviceOrientationOverride() (*gcdmessage.ChromeResponse, error) { 22 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceOrientation.clearDeviceOrientationOverride"}) 23 | } 24 | 25 | type DeviceOrientationSetDeviceOrientationOverrideParams struct { 26 | // Mock alpha 27 | Alpha float64 `json:"alpha"` 28 | // Mock beta 29 | Beta float64 `json:"beta"` 30 | // Mock gamma 31 | Gamma float64 `json:"gamma"` 32 | } 33 | 34 | // SetDeviceOrientationOverrideWithParams - Overrides the Device Orientation. 35 | func (c *DeviceOrientation) SetDeviceOrientationOverrideWithParams(v *DeviceOrientationSetDeviceOrientationOverrideParams) (*gcdmessage.ChromeResponse, error) { 36 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceOrientation.setDeviceOrientationOverride", Params: v}) 37 | } 38 | 39 | // SetDeviceOrientationOverride - Overrides the Device Orientation. 40 | // alpha - Mock alpha 41 | // beta - Mock beta 42 | // gamma - Mock gamma 43 | func (c *DeviceOrientation) SetDeviceOrientationOverride(alpha float64, beta float64, gamma float64) (*gcdmessage.ChromeResponse, error) { 44 | var v DeviceOrientationSetDeviceOrientationOverrideParams 45 | v.Alpha = alpha 46 | v.Beta = beta 47 | v.Gamma = gamma 48 | return c.SetDeviceOrientationOverrideWithParams(&v) 49 | } 50 | -------------------------------------------------------------------------------- /gcdapi/inspector.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Inspector functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Fired when remote debugging connection is about to be terminated. Contains detach reason. 12 | type InspectorDetachedEvent struct { 13 | Method string `json:"method"` 14 | Params struct { 15 | Reason string `json:"reason"` // The reason why connection has been terminated. 16 | } `json:"Params,omitempty"` 17 | } 18 | 19 | type Inspector struct { 20 | target gcdmessage.ChromeTargeter 21 | } 22 | 23 | func NewInspector(target gcdmessage.ChromeTargeter) *Inspector { 24 | c := &Inspector{target: target} 25 | return c 26 | } 27 | 28 | // Disables inspector domain notifications. 29 | func (c *Inspector) Disable() (*gcdmessage.ChromeResponse, error) { 30 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Inspector.disable"}) 31 | } 32 | 33 | // Enables inspector domain notifications. 34 | func (c *Inspector) Enable() (*gcdmessage.ChromeResponse, error) { 35 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Inspector.enable"}) 36 | } 37 | -------------------------------------------------------------------------------- /gcdapi/io.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains IO functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | type IO struct { 12 | target gcdmessage.ChromeTargeter 13 | } 14 | 15 | func NewIO(target gcdmessage.ChromeTargeter) *IO { 16 | c := &IO{target: target} 17 | return c 18 | } 19 | 20 | type IOCloseParams struct { 21 | // Handle of the stream to close. 22 | Handle string `json:"handle"` 23 | } 24 | 25 | // CloseWithParams - Close the stream, discard any temporary backing storage. 26 | func (c *IO) CloseWithParams(v *IOCloseParams) (*gcdmessage.ChromeResponse, error) { 27 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.close", Params: v}) 28 | } 29 | 30 | // Close - Close the stream, discard any temporary backing storage. 31 | // handle - Handle of the stream to close. 32 | func (c *IO) Close(handle string) (*gcdmessage.ChromeResponse, error) { 33 | var v IOCloseParams 34 | v.Handle = handle 35 | return c.CloseWithParams(&v) 36 | } 37 | 38 | type IOReadParams struct { 39 | // Handle of the stream to read. 40 | Handle string `json:"handle"` 41 | // Seek to the specified offset before reading (if not specificed, proceed with offset following the last read). Some types of streams may only support sequential reads. 42 | Offset int `json:"offset,omitempty"` 43 | // Maximum number of bytes to read (left upon the agent discretion if not specified). 44 | Size int `json:"size,omitempty"` 45 | } 46 | 47 | // ReadWithParams - Read a chunk of the stream 48 | // Returns - base64Encoded - Set if the data is base64-encoded data - Data that were read. eof - Set if the end-of-file condition occured while reading. 49 | func (c *IO) ReadWithParams(v *IOReadParams) (bool, string, bool, error) { 50 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.read", Params: v}) 51 | if err != nil { 52 | return false, "", false, err 53 | } 54 | 55 | var chromeData struct { 56 | Result struct { 57 | Base64Encoded bool 58 | Data string 59 | Eof bool 60 | } 61 | } 62 | 63 | if resp == nil { 64 | return false, "", false, &gcdmessage.ChromeEmptyResponseErr{} 65 | } 66 | 67 | // test if error first 68 | cerr := &gcdmessage.ChromeErrorResponse{} 69 | json.Unmarshal(resp.Data, cerr) 70 | if cerr != nil && cerr.Error != nil { 71 | return false, "", false, &gcdmessage.ChromeRequestErr{Resp: cerr} 72 | } 73 | 74 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 75 | return false, "", false, err 76 | } 77 | 78 | return chromeData.Result.Base64Encoded, chromeData.Result.Data, chromeData.Result.Eof, nil 79 | } 80 | 81 | // Read - Read a chunk of the stream 82 | // handle - Handle of the stream to read. 83 | // offset - Seek to the specified offset before reading (if not specificed, proceed with offset following the last read). Some types of streams may only support sequential reads. 84 | // size - Maximum number of bytes to read (left upon the agent discretion if not specified). 85 | // Returns - base64Encoded - Set if the data is base64-encoded data - Data that were read. eof - Set if the end-of-file condition occured while reading. 86 | func (c *IO) Read(handle string, offset int, size int) (bool, string, bool, error) { 87 | var v IOReadParams 88 | v.Handle = handle 89 | v.Offset = offset 90 | v.Size = size 91 | return c.ReadWithParams(&v) 92 | } 93 | 94 | type IOResolveBlobParams struct { 95 | // Object id of a Blob object wrapper. 96 | ObjectId string `json:"objectId"` 97 | } 98 | 99 | // ResolveBlobWithParams - Return UUID of Blob object specified by a remote object id. 100 | // Returns - uuid - UUID of the specified Blob. 101 | func (c *IO) ResolveBlobWithParams(v *IOResolveBlobParams) (string, error) { 102 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.resolveBlob", Params: v}) 103 | if err != nil { 104 | return "", err 105 | } 106 | 107 | var chromeData struct { 108 | Result struct { 109 | Uuid string 110 | } 111 | } 112 | 113 | if resp == nil { 114 | return "", &gcdmessage.ChromeEmptyResponseErr{} 115 | } 116 | 117 | // test if error first 118 | cerr := &gcdmessage.ChromeErrorResponse{} 119 | json.Unmarshal(resp.Data, cerr) 120 | if cerr != nil && cerr.Error != nil { 121 | return "", &gcdmessage.ChromeRequestErr{Resp: cerr} 122 | } 123 | 124 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 125 | return "", err 126 | } 127 | 128 | return chromeData.Result.Uuid, nil 129 | } 130 | 131 | // ResolveBlob - Return UUID of Blob object specified by a remote object id. 132 | // objectId - Object id of a Blob object wrapper. 133 | // Returns - uuid - UUID of the specified Blob. 134 | func (c *IO) ResolveBlob(objectId string) (string, error) { 135 | var v IOResolveBlobParams 136 | v.ObjectId = objectId 137 | return c.ResolveBlobWithParams(&v) 138 | } 139 | -------------------------------------------------------------------------------- /gcdapi/log.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Log functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Log entry. 12 | type LogLogEntry struct { 13 | Source string `json:"source"` // Log entry source. 14 | Level string `json:"level"` // Log entry severity. 15 | Text string `json:"text"` // Logged text. 16 | Timestamp float64 `json:"timestamp"` // Timestamp when this entry was added. 17 | Url string `json:"url,omitempty"` // URL of the resource if known. 18 | LineNumber int `json:"lineNumber,omitempty"` // Line number in the resource. 19 | StackTrace *RuntimeStackTrace `json:"stackTrace,omitempty"` // JavaScript stack trace. 20 | NetworkRequestId string `json:"networkRequestId,omitempty"` // Identifier of the network request associated with this entry. 21 | WorkerId string `json:"workerId,omitempty"` // Identifier of the worker associated with this entry. 22 | Args []*RuntimeRemoteObject `json:"args,omitempty"` // Call arguments. 23 | } 24 | 25 | // Violation configuration setting. 26 | type LogViolationSetting struct { 27 | Name string `json:"name"` // Violation type. 28 | Threshold float64 `json:"threshold"` // Time threshold to trigger upon. 29 | } 30 | 31 | // Issued when new message was logged. 32 | type LogEntryAddedEvent struct { 33 | Method string `json:"method"` 34 | Params struct { 35 | Entry *LogLogEntry `json:"entry"` // The entry. 36 | } `json:"Params,omitempty"` 37 | } 38 | 39 | type Log struct { 40 | target gcdmessage.ChromeTargeter 41 | } 42 | 43 | func NewLog(target gcdmessage.ChromeTargeter) *Log { 44 | c := &Log{target: target} 45 | return c 46 | } 47 | 48 | // Clears the log. 49 | func (c *Log) Clear() (*gcdmessage.ChromeResponse, error) { 50 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.clear"}) 51 | } 52 | 53 | // Disables log domain, prevents further log entries from being reported to the client. 54 | func (c *Log) Disable() (*gcdmessage.ChromeResponse, error) { 55 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.disable"}) 56 | } 57 | 58 | // Enables log domain, sends the entries collected so far to the client by means of the `entryAdded` notification. 59 | func (c *Log) Enable() (*gcdmessage.ChromeResponse, error) { 60 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.enable"}) 61 | } 62 | 63 | type LogStartViolationsReportParams struct { 64 | // Configuration for violations. 65 | Config []*LogViolationSetting `json:"config"` 66 | } 67 | 68 | // StartViolationsReportWithParams - start violation reporting. 69 | func (c *Log) StartViolationsReportWithParams(v *LogStartViolationsReportParams) (*gcdmessage.ChromeResponse, error) { 70 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.startViolationsReport", Params: v}) 71 | } 72 | 73 | // StartViolationsReport - start violation reporting. 74 | // config - Configuration for violations. 75 | func (c *Log) StartViolationsReport(config []*LogViolationSetting) (*gcdmessage.ChromeResponse, error) { 76 | var v LogStartViolationsReportParams 77 | v.Config = config 78 | return c.StartViolationsReportWithParams(&v) 79 | } 80 | 81 | // Stop violation reporting. 82 | func (c *Log) StopViolationsReport() (*gcdmessage.ChromeResponse, error) { 83 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.stopViolationsReport"}) 84 | } 85 | -------------------------------------------------------------------------------- /gcdapi/media.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Media functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Have one type per entry in MediaLogRecord::Type Corresponds to kMessage 12 | type MediaPlayerMessage struct { 13 | Level string `json:"level"` // Keep in sync with MediaLogMessageLevel We are currently keeping the message level 'error' separate from the PlayerError type because right now they represent different things, this one being a DVLOG(ERROR) style log message that gets printed based on what log level is selected in the UI, and the other is a representation of a media::PipelineStatus object. Soon however we're going to be moving away from using PipelineStatus for errors and introducing a new error type which should hopefully let us integrate the error log level into the PlayerError type. 14 | Message string `json:"message"` // 15 | } 16 | 17 | // Corresponds to kMediaPropertyChange 18 | type MediaPlayerProperty struct { 19 | Name string `json:"name"` // 20 | Value string `json:"value"` // 21 | } 22 | 23 | // Corresponds to kMediaEventTriggered 24 | type MediaPlayerEvent struct { 25 | Timestamp float64 `json:"timestamp"` // 26 | Value string `json:"value"` // 27 | } 28 | 29 | // Corresponds to kMediaError 30 | type MediaPlayerError struct { 31 | Type string `json:"type"` // 32 | ErrorCode string `json:"errorCode"` // When this switches to using media::Status instead of PipelineStatus we can remove "errorCode" and replace it with the fields from a Status instance. This also seems like a duplicate of the error level enum - there is a todo bug to have that level removed and use this instead. (crbug.com/1068454) 33 | } 34 | 35 | // This can be called multiple times, and can be used to set / override / remove player properties. A null propValue indicates removal. 36 | type MediaPlayerPropertiesChangedEvent struct { 37 | Method string `json:"method"` 38 | Params struct { 39 | PlayerId string `json:"playerId"` // 40 | Properties []*MediaPlayerProperty `json:"properties"` // 41 | } `json:"Params,omitempty"` 42 | } 43 | 44 | // Send events as a list, allowing them to be batched on the browser for less congestion. If batched, events must ALWAYS be in chronological order. 45 | type MediaPlayerEventsAddedEvent struct { 46 | Method string `json:"method"` 47 | Params struct { 48 | PlayerId string `json:"playerId"` // 49 | Events []*MediaPlayerEvent `json:"events"` // 50 | } `json:"Params,omitempty"` 51 | } 52 | 53 | // Send a list of any messages that need to be delivered. 54 | type MediaPlayerMessagesLoggedEvent struct { 55 | Method string `json:"method"` 56 | Params struct { 57 | PlayerId string `json:"playerId"` // 58 | Messages []*MediaPlayerMessage `json:"messages"` // 59 | } `json:"Params,omitempty"` 60 | } 61 | 62 | // Send a list of any errors that need to be delivered. 63 | type MediaPlayerErrorsRaisedEvent struct { 64 | Method string `json:"method"` 65 | Params struct { 66 | PlayerId string `json:"playerId"` // 67 | Errors []*MediaPlayerError `json:"errors"` // 68 | } `json:"Params,omitempty"` 69 | } 70 | 71 | // Called whenever a player is created, or when a new agent joins and recieves a list of active players. If an agent is restored, it will recieve the full list of player ids and all events again. 72 | type MediaPlayersCreatedEvent struct { 73 | Method string `json:"method"` 74 | Params struct { 75 | Players []string `json:"players"` // 76 | } `json:"Params,omitempty"` 77 | } 78 | 79 | type Media struct { 80 | target gcdmessage.ChromeTargeter 81 | } 82 | 83 | func NewMedia(target gcdmessage.ChromeTargeter) *Media { 84 | c := &Media{target: target} 85 | return c 86 | } 87 | 88 | // Enables the Media domain 89 | func (c *Media) Enable() (*gcdmessage.ChromeResponse, error) { 90 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Media.enable"}) 91 | } 92 | 93 | // Disables the Media domain. 94 | func (c *Media) Disable() (*gcdmessage.ChromeResponse, error) { 95 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Media.disable"}) 96 | } 97 | -------------------------------------------------------------------------------- /gcdapi/performance.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Performance functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Run-time execution metric. 12 | type PerformanceMetric struct { 13 | Name string `json:"name"` // Metric name. 14 | Value float64 `json:"value"` // Metric value. 15 | } 16 | 17 | // Current values of the metrics. 18 | type PerformanceMetricsEvent struct { 19 | Method string `json:"method"` 20 | Params struct { 21 | Metrics []*PerformanceMetric `json:"metrics"` // Current values of the metrics. 22 | Title string `json:"title"` // Timestamp title. 23 | } `json:"Params,omitempty"` 24 | } 25 | 26 | type Performance struct { 27 | target gcdmessage.ChromeTargeter 28 | } 29 | 30 | func NewPerformance(target gcdmessage.ChromeTargeter) *Performance { 31 | c := &Performance{target: target} 32 | return c 33 | } 34 | 35 | // Disable collecting and reporting metrics. 36 | func (c *Performance) Disable() (*gcdmessage.ChromeResponse, error) { 37 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.disable"}) 38 | } 39 | 40 | type PerformanceEnableParams struct { 41 | // Time domain to use for collecting and reporting duration metrics. 42 | TimeDomain string `json:"timeDomain,omitempty"` 43 | } 44 | 45 | // EnableWithParams - Enable collecting and reporting metrics. 46 | func (c *Performance) EnableWithParams(v *PerformanceEnableParams) (*gcdmessage.ChromeResponse, error) { 47 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.enable", Params: v}) 48 | } 49 | 50 | // Enable - Enable collecting and reporting metrics. 51 | // timeDomain - Time domain to use for collecting and reporting duration metrics. 52 | func (c *Performance) Enable(timeDomain string) (*gcdmessage.ChromeResponse, error) { 53 | var v PerformanceEnableParams 54 | v.TimeDomain = timeDomain 55 | return c.EnableWithParams(&v) 56 | } 57 | 58 | type PerformanceSetTimeDomainParams struct { 59 | // Time domain 60 | TimeDomain string `json:"timeDomain"` 61 | } 62 | 63 | // SetTimeDomainWithParams - Sets time domain to use for collecting and reporting duration metrics. Note that this must be called before enabling metrics collection. Calling this method while metrics collection is enabled returns an error. 64 | func (c *Performance) SetTimeDomainWithParams(v *PerformanceSetTimeDomainParams) (*gcdmessage.ChromeResponse, error) { 65 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.setTimeDomain", Params: v}) 66 | } 67 | 68 | // SetTimeDomain - Sets time domain to use for collecting and reporting duration metrics. Note that this must be called before enabling metrics collection. Calling this method while metrics collection is enabled returns an error. 69 | // timeDomain - Time domain 70 | func (c *Performance) SetTimeDomain(timeDomain string) (*gcdmessage.ChromeResponse, error) { 71 | var v PerformanceSetTimeDomainParams 72 | v.TimeDomain = timeDomain 73 | return c.SetTimeDomainWithParams(&v) 74 | } 75 | 76 | // GetMetrics - Retrieve current values of run-time metrics. 77 | // Returns - metrics - Current values for run-time metrics. 78 | func (c *Performance) GetMetrics() ([]*PerformanceMetric, error) { 79 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.getMetrics"}) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | var chromeData struct { 85 | Result struct { 86 | Metrics []*PerformanceMetric 87 | } 88 | } 89 | 90 | if resp == nil { 91 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 92 | } 93 | 94 | // test if error first 95 | cerr := &gcdmessage.ChromeErrorResponse{} 96 | json.Unmarshal(resp.Data, cerr) 97 | if cerr != nil && cerr.Error != nil { 98 | return nil, &gcdmessage.ChromeRequestErr{Resp: cerr} 99 | } 100 | 101 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 102 | return nil, err 103 | } 104 | 105 | return chromeData.Result.Metrics, nil 106 | } 107 | -------------------------------------------------------------------------------- /gcdapi/schema.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Schema functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Description of the protocol domain. 12 | type SchemaDomain struct { 13 | Name string `json:"name"` // Domain name. 14 | Version string `json:"version"` // Domain version. 15 | } 16 | 17 | type Schema struct { 18 | target gcdmessage.ChromeTargeter 19 | } 20 | 21 | func NewSchema(target gcdmessage.ChromeTargeter) *Schema { 22 | c := &Schema{target: target} 23 | return c 24 | } 25 | 26 | // GetDomains - Returns supported domains. 27 | // Returns - domains - List of supported domains. 28 | func (c *Schema) GetDomains() ([]*SchemaDomain, error) { 29 | resp, err := gcdmessage.SendCustomReturn(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Schema.getDomains"}) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | var chromeData struct { 35 | Result struct { 36 | Domains []*SchemaDomain 37 | } 38 | } 39 | 40 | if resp == nil { 41 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 42 | } 43 | 44 | // test if error first 45 | cerr := &gcdmessage.ChromeErrorResponse{} 46 | json.Unmarshal(resp.Data, cerr) 47 | if cerr != nil && cerr.Error != nil { 48 | return nil, &gcdmessage.ChromeRequestErr{Resp: cerr} 49 | } 50 | 51 | if err := json.Unmarshal(resp.Data, &chromeData); err != nil { 52 | return nil, err 53 | } 54 | 55 | return chromeData.Result.Domains, nil 56 | } 57 | -------------------------------------------------------------------------------- /gcdapi/tethering.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Tethering functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "github.com/wirepair/gcd/gcdmessage" 9 | ) 10 | 11 | // Informs that port was successfully bound and got a specified connection id. 12 | type TetheringAcceptedEvent struct { 13 | Method string `json:"method"` 14 | Params struct { 15 | Port int `json:"port"` // Port number that was successfully bound. 16 | ConnectionId string `json:"connectionId"` // Connection id to be used. 17 | } `json:"Params,omitempty"` 18 | } 19 | 20 | type Tethering struct { 21 | target gcdmessage.ChromeTargeter 22 | } 23 | 24 | func NewTethering(target gcdmessage.ChromeTargeter) *Tethering { 25 | c := &Tethering{target: target} 26 | return c 27 | } 28 | 29 | type TetheringBindParams struct { 30 | // Port number to bind. 31 | Port int `json:"port"` 32 | } 33 | 34 | // BindWithParams - Request browser port binding. 35 | func (c *Tethering) BindWithParams(v *TetheringBindParams) (*gcdmessage.ChromeResponse, error) { 36 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Tethering.bind", Params: v}) 37 | } 38 | 39 | // Bind - Request browser port binding. 40 | // port - Port number to bind. 41 | func (c *Tethering) Bind(port int) (*gcdmessage.ChromeResponse, error) { 42 | var v TetheringBindParams 43 | v.Port = port 44 | return c.BindWithParams(&v) 45 | } 46 | 47 | type TetheringUnbindParams struct { 48 | // Port number to unbind. 49 | Port int `json:"port"` 50 | } 51 | 52 | // UnbindWithParams - Request browser port unbinding. 53 | func (c *Tethering) UnbindWithParams(v *TetheringUnbindParams) (*gcdmessage.ChromeResponse, error) { 54 | return gcdmessage.SendDefaultRequest(c.target, c.target.GetSendCh(), &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Tethering.unbind", Params: v}) 55 | } 56 | 57 | // Unbind - Request browser port unbinding. 58 | // port - Port number to unbind. 59 | func (c *Tethering) Unbind(port int) (*gcdmessage.ChromeResponse, error) { 60 | var v TetheringUnbindParams 61 | v.Port = port 62 | return c.UnbindWithParams(&v) 63 | } 64 | -------------------------------------------------------------------------------- /gcdapi/version.go: -------------------------------------------------------------------------------- 1 | package gcdapi 2 | 3 | import "github.com/json-iterator/go" 4 | 5 | // define json here for all Domains 6 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 7 | 8 | // Chrome Channel information 9 | const CHROME_CHANNEL = "stable" 10 | // Chrome Version information 11 | const CHROME_VERSION = "83.0.4103.116" -------------------------------------------------------------------------------- /gcdapigen/README.md: -------------------------------------------------------------------------------- 1 | # Google Chrome Debugger (GCD) API Generator 2 | You should hopefully not have to use this, but this package is for generating commands, types and events for the Google Chrome Debugger Service for remotely debugging a Chrome Process. 3 | 4 | Basically it reads in the protocol.json file, does some gnarly processing and spits out the code using templates. All references are looked up so we don't have to do nasty type conversions just to get to a base underlying type. This was pretty much a full rewrite, moved all API stuff to it's own package so it doesn't pollute the gcd package. 5 | 6 | Updated September 2015 with latest protocol.json. 7 | 8 | If you plan on downloading later versions of protocol.json, keep in mind there are some things you'll need to fix by hand. In particular one field which *should* be a bool is actually a string of "true". Go's JSON error handling is absolutely horrible in that it won't tell you the exact line the unmarshalling fails on so I had to fix it by hand. 9 | 10 | ## Developer Notes 11 | The protocol.json file while appears simple at first is pretty complex, with lots of crazy edge cases and variables, all around how they use '$ref' fields. You'll notice a *lot* of conditionals when looking up references, i just saw no other way to accomplish generating types without having hundereds of dumb type SomeObject string or type SomeOtherObject []int everywhere. To make using the API easier, i resolved all of these references (at least I think I did) and tried to limit the amount of work necessary for unmarshalling the various data types requires. 12 | 13 | If you need to fix references, the two main areas are utils.go:PopulateReferences and domain.go:resolveReferences. We first loop over the entire set of domains and make a map of all reference types, storing various critical metadata about the references (is it array, is it a ref to a underlying type, etc). As types are generated, it looks at this map and replaces any definitions with the proper ones. 14 | 15 | ## Usage 16 | Download the latest [protocol.json](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/devtools/protocol.json&q=protocol.json&sq=package:chromium&type=cs) and make sure there aren't any string encoded bools (usually hidden or optional fields), fix if necessary, then run: 17 | ``` 18 | go build 19 | ./gcdapigen protocol.json 20 | cp -R output/. ../gcdapi 21 | cd !$ 22 | go build && go install 23 | ``` 24 | 25 | ## Licensing 26 | The MIT License (MIT) 27 | 28 | Copyright (c) 2015 isaac dawson 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy 31 | of this software and associated documentation files (the "Software"), to deal 32 | in the Software without restriction, including without limitation the rights 33 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | copies of the Software, and to permit persons to whom the Software is 35 | furnished to do so, subject to the following conditions: 36 | 37 | The above copyright notice and this permission notice shall be included in 38 | all copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | THE SOFTWARE. 47 | -------------------------------------------------------------------------------- /gcdapigen/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go build 4 | ./gcdapigen protocol.json 5 | cp -R output/. ../gcdapi 6 | cd ../ && go build && go install 7 | -------------------------------------------------------------------------------- /gcdapigen/command.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Command struct { 28 | Name string 29 | Description string 30 | Parameters []*TypeProperties 31 | Returns []*Return 32 | HasParams bool 33 | HasReturn bool 34 | NoParamReturnCalls bool 35 | ParamCalls bool 36 | ReturnCalls bool 37 | ParamReturnCalls bool 38 | } 39 | 40 | func NewCommand(protoCommand *ProtoCommand) *Command { 41 | c := &Command{} 42 | c.Name = protoCommand.Name 43 | c.Description = protoCommand.Description 44 | if protoCommand.Parameters != nil && len(protoCommand.Parameters) > 0 { 45 | c.HasParams = true 46 | } 47 | 48 | if protoCommand.Returns != nil && len(protoCommand.Returns) > 0 { 49 | c.HasReturn = true 50 | } 51 | // Determine type of call for template output 52 | if c.HasParams == false && c.HasReturn == false { 53 | c.NoParamReturnCalls = true 54 | } 55 | 56 | if c.HasParams == true && c.HasReturn == false { 57 | c.ParamCalls = true 58 | } 59 | 60 | if c.HasParams == false && c.HasReturn == true { 61 | c.ReturnCalls = true 62 | } 63 | 64 | if c.HasParams == true && c.HasReturn == true { 65 | c.ParamReturnCalls = true 66 | } 67 | 68 | c.Returns = make([]*Return, 0) 69 | c.Parameters = make([]*TypeProperties, 0) 70 | return c 71 | } 72 | -------------------------------------------------------------------------------- /gcdapigen/event.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Event struct { 28 | protoEvent *ProtoEvent 29 | Name string 30 | Description string 31 | Parameters []*TypeProperties 32 | HasParams bool 33 | } 34 | 35 | func NewEvent(protoEvent *ProtoEvent) *Event { 36 | e := &Event{} 37 | e.Name = protoEvent.Name 38 | e.Description = protoEvent.Description 39 | if protoEvent.Parameters != nil && len(protoEvent.Parameters) > 0 { 40 | e.HasParams = true 41 | } 42 | e.Parameters = make([]*TypeProperties, 0) 43 | return e 44 | } 45 | -------------------------------------------------------------------------------- /gcdapigen/return.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Return struct { 28 | protoReturn *ProtoCommandReturns 29 | Name string // property name 30 | Description string // property description 31 | UnderlyingType string 32 | GoType string 33 | Optional bool // is this property optional? 34 | EnumVals string // possible enum values as a string 35 | IsRef bool // is a reference to another type 36 | IsTypeArray bool // for templates to spit out [] 37 | IsPointer bool 38 | Ref string 39 | } 40 | 41 | func NewReturn(protoReturn *ProtoCommandReturns) *Return { 42 | r := &Return{} 43 | r.protoReturn = protoReturn 44 | r.Name = protoReturn.Name 45 | r.Description = protoReturn.Description 46 | r.UnderlyingType = protoReturn.Type 47 | r.Ref = protoReturn.Ref 48 | 49 | if protoReturn.Ref != "" { 50 | r.IsRef = true 51 | } 52 | 53 | // if array, check underlying array type to see if it's a reference 54 | if r.IsArray() { 55 | r.IsTypeArray = true 56 | if arrayRef := r.ArrayRef(); arrayRef != "" { 57 | r.Ref = arrayRef 58 | r.IsRef = true 59 | } 60 | } 61 | 62 | return r 63 | } 64 | 65 | func (r *Return) ArrayRef() string { 66 | if r.protoReturn.Items.Ref != "" { 67 | return r.protoReturn.Items.Ref 68 | } 69 | return "" 70 | } 71 | 72 | // PropSetter interface methods 73 | func (r *Return) GetGoType() string { 74 | return r.GoType 75 | } 76 | 77 | func (r *Return) SetIsTypeArray(isTypeArray bool) { 78 | r.IsTypeArray = true 79 | } 80 | 81 | func (r *Return) GetEnumVals() string { 82 | return r.EnumVals 83 | } 84 | 85 | func (r *Return) GetRef() string { 86 | return r.Ref 87 | } 88 | 89 | func (r *Return) SetIsRef(isRef bool) { 90 | r.IsRef = isRef 91 | } 92 | 93 | func (r *Return) GetIsRef() bool { 94 | return r.IsRef 95 | } 96 | 97 | func (r *Return) SetGoType(goType string) { 98 | r.GoType = goType 99 | } 100 | 101 | func (r *Return) GetDescription() string { 102 | return r.Description 103 | } 104 | func (r *Return) SetDescription(description string) { 105 | r.Description = description 106 | } 107 | 108 | func (r *Return) SetPointerType(isPointer bool) { 109 | r.IsPointer = isPointer 110 | } 111 | 112 | // SharedProperties interface methods 113 | func (r *Return) IsNonPropertiesObject() bool { 114 | return (r.UnderlyingType == "object") // return with object type never has properties // && len(r.protoReturn.Properties) == 0) 115 | } 116 | 117 | func (r *Return) GetUnderlyingType() string { 118 | return r.UnderlyingType 119 | } 120 | 121 | func (r *Return) IsArray() bool { 122 | return r.UnderlyingType == "array" 123 | } 124 | 125 | func (r *Return) GetArrayType() string { 126 | if r.protoReturn.Items.Type != "" { 127 | return r.protoReturn.Items.Type 128 | } 129 | 130 | if r.protoReturn.Items.Ref != "" { 131 | return r.protoReturn.Items.Ref 132 | } 133 | return "object" 134 | } 135 | -------------------------------------------------------------------------------- /gcdapigen/template_funcs.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "strings" 29 | ) 30 | 31 | func modifyReserved(input string) string { 32 | switch input { 33 | case "type": 34 | return "theType" 35 | case "range": 36 | return "theRange" 37 | case "interface": 38 | return "theInterface" 39 | case "for": 40 | return "theFor" 41 | } 42 | return input 43 | } 44 | 45 | func nullType(input string) string { 46 | if strings.Contains(input, "[]") { 47 | return "nil" 48 | } 49 | //fmt.Printf("INPUT: %s\n", input) 50 | switch input { 51 | case "int": 52 | return "0" 53 | case "float64": 54 | return "0" 55 | case "string": 56 | return "\"\"" 57 | case "bool": 58 | return "false" 59 | case "interface{}": 60 | return "nil" 61 | } 62 | return "nil" 63 | } 64 | -------------------------------------------------------------------------------- /gcdapigen/type.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "strings" 29 | ) 30 | 31 | type Type struct { 32 | protoType *ProtoType 33 | Name string // the name of the API call, Event or Type 34 | Description string // the description/comments for the struct 35 | GoType string // the type this would be in Go 36 | UnderlyingType string // the type defined in protocol.json 37 | EnumVals string // if it's an enum string list out the possible values as a comment 38 | IsSubType bool // is this a sub type? (Should be prefixed with Sub in template) 39 | Properties []*TypeProperties 40 | } 41 | 42 | func NewType(protoType *ProtoType) *Type { 43 | t := &Type{} 44 | t.protoType = protoType 45 | t.Name = protoType.Id 46 | t.Description = protoType.Description 47 | if t.Description == "" { 48 | t.Description = "No Description." 49 | } 50 | t.UnderlyingType = protoType.Type 51 | t.Properties = make([]*TypeProperties, 0) 52 | return t 53 | } 54 | 55 | func NewSubType(parentProps *TypeProperties, protoProps *ProtoProperty) *Type { 56 | st := &Type{} 57 | st.IsSubType = true 58 | // only convert to type if it's an array 59 | if protoProps.IsArray() { 60 | st.protoType = typeFromProperties(protoProps) 61 | } 62 | 63 | st.Name = "Sub" + strings.Title(parentProps.Name) 64 | st.Properties = make([]*TypeProperties, 0) 65 | st.UnderlyingType = parentProps.UnderlyingType 66 | return st 67 | } 68 | 69 | func (t *Type) IsNonPropertiesObject() bool { 70 | return (t.UnderlyingType == "object" && len(t.protoType.Properties) == 0) 71 | } 72 | 73 | func (t *Type) GetUnderlyingType() string { 74 | return t.UnderlyingType 75 | } 76 | 77 | func (t *Type) IsArray() bool { 78 | return t.UnderlyingType == "array" 79 | } 80 | 81 | func (t *Type) GetArrayType() string { 82 | if t.protoType.Items.Type != "" { 83 | return t.protoType.Items.Type 84 | } 85 | 86 | if t.protoType.Items.Ref != "" { 87 | return t.protoType.Items.Ref 88 | } 89 | return "object" 90 | } 91 | -------------------------------------------------------------------------------- /gcdapigen/type_properties.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | // Used for Types and Parameters to function calls 28 | type TypeProperties struct { 29 | protoProperty *ProtoProperty 30 | Name string // property name 31 | Description string // property description 32 | UnderlyingType string 33 | GoType string 34 | Optional bool // is this property optional? 35 | EnumVals string // possible enum values as a string 36 | Ref string 37 | IsRef bool // is a reference to another type 38 | IsPointer bool // should we output as pointer (for API types, not basic types) 39 | IsTypeArray bool // for templates to spit out [] 40 | } 41 | 42 | func NewTypeProperties(props *ProtoProperty) *TypeProperties { 43 | tp := &TypeProperties{} 44 | tp.protoProperty = props 45 | tp.Name = props.Name 46 | tp.Description = props.Description 47 | tp.Optional = props.Optional 48 | tp.Ref = props.Ref 49 | tp.UnderlyingType = props.Type 50 | if tp.IsArray() { 51 | tp.IsTypeArray = true 52 | if arrayRef := tp.ArrayRef(); arrayRef != "" { 53 | tp.Ref = arrayRef 54 | tp.IsRef = true 55 | } 56 | } 57 | return tp 58 | } 59 | 60 | // PropSetter interface methods 61 | func (p *TypeProperties) GetGoType() string { 62 | return p.GoType 63 | } 64 | 65 | func (p *TypeProperties) SetGoType(goType string) { 66 | p.GoType = goType 67 | } 68 | 69 | func (p *TypeProperties) GetIsRef() bool { 70 | return p.IsRef 71 | } 72 | 73 | func (p *TypeProperties) SetIsRef(isRef bool) { 74 | p.IsRef = isRef 75 | } 76 | 77 | func (p *TypeProperties) SetIsTypeArray(isTypeArray bool) { 78 | p.IsTypeArray = true 79 | } 80 | 81 | func (p *TypeProperties) GetRef() string { 82 | return p.Ref 83 | } 84 | 85 | func (p *TypeProperties) GetEnumVals() string { 86 | return p.EnumVals 87 | } 88 | 89 | func (p *TypeProperties) GetDescription() string { 90 | return p.Description 91 | } 92 | func (p *TypeProperties) SetDescription(description string) { 93 | p.Description = description 94 | } 95 | 96 | func (p *TypeProperties) SetPointerType(isPointer bool) { 97 | p.IsPointer = isPointer 98 | } 99 | 100 | // SharedProperties interface methods 101 | func (p *TypeProperties) IsNonPropertiesObject() bool { 102 | return (p.UnderlyingType == "object" && len(p.protoProperty.Properties) == 0) 103 | } 104 | 105 | func (p *TypeProperties) GetUnderlyingType() string { 106 | return p.UnderlyingType 107 | } 108 | 109 | func (p *TypeProperties) IsArray() bool { 110 | return p.UnderlyingType == "array" 111 | } 112 | 113 | func (p *TypeProperties) GetArrayType() string { 114 | if p.protoProperty.Items.Type != "" { 115 | return p.protoProperty.Items.Type 116 | } 117 | 118 | if p.protoProperty.Items.Ref != "" { 119 | return p.protoProperty.Items.Ref 120 | } 121 | return "object" 122 | } 123 | 124 | func (p *TypeProperties) ArrayRef() string { 125 | if p.protoProperty.Items.Ref != "" { 126 | return p.protoProperty.Items.Ref 127 | } 128 | return "" 129 | } 130 | -------------------------------------------------------------------------------- /gcdapigen/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "fmt" 29 | "strings" 30 | ) 31 | 32 | type PropSetter interface { 33 | GetRef() string 34 | GetIsRef() bool 35 | GetGoType() string 36 | GetEnumVals() string 37 | SetGoType(goType string) 38 | SetIsTypeArray(isTypeArray bool) 39 | GetDescription() string 40 | SetDescription(description string) 41 | SetIsRef(isRef bool) 42 | SetPointerType(isPointer bool) 43 | } 44 | 45 | type SharedProperties interface { 46 | GetUnderlyingType() string 47 | GetArrayType() string 48 | IsArray() bool 49 | IsNonPropertiesObject() bool 50 | } 51 | 52 | // Loops over the domain ProtoType slice to create a reference map that we can use 53 | // to resolve references when we go to spit out templates. We check if the type is a base 54 | // type like: 55 | // { 56 | // "id": "RequestId", 57 | // "type": "string", 58 | // "description": "Unique request identifier." 59 | // }, 60 | // Instead of spitting out "type NetworkRequestId string" and having other structs 61 | // include references to it, we resolve any references directly to 'string' 62 | // this makes working with the API far easier (no stupid type conversions everywhere) 63 | func PopulateReferences(domain string, types []*ProtoType) { 64 | for _, protoType := range types { 65 | ref := &GlobalReference{} 66 | ref.LocalRefName = protoType.Id 67 | if isBaseType(protoType) { 68 | ref.IsBaseType = true 69 | ref.BaseType = getGoType(protoType) 70 | } 71 | 72 | ref.ExternalGoName = domain + protoType.Id 73 | // an array pointing to another type 74 | if protoType.IsArray() { 75 | arrType := protoType.GetArrayType() 76 | ref.IsArrayRef = true 77 | ref.ExternalGoName = domain + arrType 78 | ref.RefToRefName = domain + "." + arrType 79 | } 80 | 81 | if len(protoType.Enum) > 0 { 82 | ref.EnumDescription = " enum values: " + strings.Join(protoType.Enum, ", ") 83 | } 84 | globalRefs[domain+"."+protoType.Id] = ref 85 | 86 | if ref.IsBaseType == false { 87 | populateSubReferences(domain, protoType) 88 | } 89 | } 90 | } 91 | 92 | // Check if the type has a nested type and create a new sub type for it and add to our 93 | // reference map. 94 | func populateSubReferences(domain string, protoType *ProtoType) { 95 | for _, prop := range protoType.Properties { 96 | // a new SubType 97 | if isSubType(prop) { 98 | prefix := "Sub" + strings.Title(prop.Name) 99 | ref := &GlobalReference{} 100 | ref.LocalRefName = prop.Name 101 | ref.ExternalGoName = domain + prefix 102 | globalRefs[domain+prefix] = ref 103 | continue 104 | } 105 | 106 | } 107 | } 108 | 109 | // Get the go type, if it is an array we look up the underlying type of the array. 110 | func getGoType(props SharedProperties) string { 111 | 112 | protoType := props.GetUnderlyingType() 113 | if props.IsArray() { 114 | protoType = props.GetArrayType() 115 | } 116 | 117 | returnType := "" 118 | switch protoType { 119 | case "string", "enum": 120 | returnType = "string" 121 | case "integer": 122 | returnType = "int" 123 | case "number": 124 | returnType = "float64" 125 | case "object": 126 | returnType = "map[string]interface{}" // default 127 | case "any": 128 | returnType = "interface{}" 129 | case "boolean": 130 | returnType = "bool" 131 | } 132 | return returnType 133 | } 134 | 135 | // Is this a base type? If NoPropertiesObject we just set it to map[string]interface{} 136 | // If it's an array, we look up if the array underlying type is a base type. 137 | func isBaseType(props SharedProperties) bool { 138 | if props.IsNonPropertiesObject() { 139 | return true 140 | } 141 | 142 | underlyingType := props.GetUnderlyingType() 143 | if props.IsArray() { 144 | underlyingType = props.GetArrayType() 145 | } 146 | 147 | switch underlyingType { 148 | case "any", "string", "enum", "integer", "number", "boolean": 149 | return true 150 | } 151 | return false 152 | } 153 | 154 | // Do the properties of this type point to a nested type? Can be array or object that nests 155 | // a sub type. 156 | func isSubType(protoProp *ProtoProperty) bool { 157 | if (protoProp.Type == "object" && !protoProp.IsNonPropertiesObject()) || (protoProp.Type == "array" && protoProp.Items.Type == "object") { 158 | return true 159 | } 160 | return false 161 | } 162 | 163 | func isPointerType(props PropSetter) bool { 164 | goType := props.GetGoType() 165 | switch goType { 166 | case "int", "string", "bool", "float64", "map[string]interface{}", "interface{}": 167 | return false 168 | } 169 | return true 170 | } 171 | 172 | // used for nested arrays, convert properties to a type 173 | func typeFromProperties(protoProps *ProtoProperty) *ProtoType { 174 | t := &ProtoType{} 175 | t.Type = protoProps.Type 176 | t.Enum = protoProps.Enum 177 | t.Hidden = protoProps.Hidden 178 | t.Properties = protoProps.Items.Properties 179 | t.Id = protoProps.Name 180 | t.Description = protoProps.Description 181 | fmt.Printf("RETURNING NEW TYPE FROM PROPS: %#v\n", t) 182 | return t 183 | } 184 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wirepair/gcd 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect 7 | github.com/gobwas/pool v0.2.0 // indirect 8 | github.com/gobwas/ws v1.0.3 9 | github.com/json-iterator/go v1.1.9 10 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 11 | github.com/modern-go/reflect2 v1.0.1 // indirect 12 | github.com/stretchr/testify v1.4.0 // indirect 13 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect 14 | gopkg.in/yaml.v2 v2.2.8 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= 5 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 6 | github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= 7 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 8 | github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g= 9 | github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 10 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 11 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 12 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 13 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 14 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 15 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 16 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 17 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 18 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 19 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 20 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 24 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 25 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 26 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 27 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 28 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 31 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 32 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 33 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 34 | -------------------------------------------------------------------------------- /testdata/console_log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | console log 6 | 11 | 12 | 13 |
console log
14 | 15 | -------------------------------------------------------------------------------- /testdata/cookie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 |
set cookie
18 | 19 | 20 | -------------------------------------------------------------------------------- /testdata/extension/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /testdata/extension/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.0.6 2 | - Fix the extension not working for https://web.whatsapp.com 3 | 4 | 1.0.5 5 | - Fix manifest description being too long from v1.0.4 6 | 7 | 1.0.4 8 | - CSP headers are enabled by default when you install this extension. You must 9 | click the extention's button to disable CSP. 10 | 11 | 1.0.3 12 | - Fixes bad extension packaging of 1.0.2. Do not use 1.0.2. 13 | 14 | 1.0.2 15 | - Make the extension work for iframes. 16 | 17 | 1.0.1 18 | - Initial version. 19 | -------------------------------------------------------------------------------- /testdata/extension/README.md: -------------------------------------------------------------------------------- 1 | Disable Content-Security-Policy in Chromium browers for web application testing. 2 | 3 | [Install via the Chrome Web Store](https://chrome.google.com/webstore/detail/disable-content-security/ieelmcmcagommplceebfedjlakkhpden) 4 | 5 | ## Contributors 6 | 7 | * [Phil Grayson](https://github.com/PhilGrayson) 8 | * [Denis Gorbachev](https://github.com/DenisGorbachev) 9 | -------------------------------------------------------------------------------- /testdata/extension/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Disable Content-Security-Policy for web application testing. When the icon is colored, CSP headers are disabled.", 4 | "description": "The description of this extention" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /testdata/extension/_locales/en_GB/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Disable Content-Security-Policy for web application testing. When the icon is coloured, CSP headers are disabled.", 4 | "description": "The description of this extention" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /testdata/extension/background.js: -------------------------------------------------------------------------------- 1 | var isCSPDisabled = false; 2 | 3 | var onHeadersReceived = function(details) { 4 | if (!isCSPDisabled) { 5 | return; 6 | } 7 | 8 | for (var i = 0; i < details.responseHeaders.length; i++) { 9 | if ('content-security-policy' === details.responseHeaders[i].name.toLowerCase()) { 10 | details.responseHeaders[i].value = ''; 11 | } 12 | } 13 | 14 | return { 15 | responseHeaders: details.responseHeaders 16 | }; 17 | }; 18 | 19 | var updateUI = function() { 20 | var iconName = isCSPDisabled ? 'on' : 'off'; 21 | var title = isCSPDisabled ? 'disabled' : 'enabled'; 22 | 23 | chrome.browserAction.setIcon({ path: "images/icon38-" + iconName + ".png" }); 24 | chrome.browserAction.setTitle({ title: 'Content-Security-Policy headers are ' + title }); 25 | }; 26 | 27 | var filter = { 28 | urls: ["*://*/*"], 29 | types: ["main_frame", "sub_frame"] 30 | }; 31 | 32 | chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, ["blocking", "responseHeaders"]); 33 | 34 | chrome.browserAction.onClicked.addListener(function() { 35 | isCSPDisabled = !isCSPDisabled; 36 | 37 | if (isCSPDisabled) { 38 | chrome.browsingData.remove({}, {"serviceWorkers": true}, function () {}); 39 | } 40 | 41 | updateUI() 42 | }); 43 | 44 | updateUI(); 45 | -------------------------------------------------------------------------------- /testdata/extension/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/testdata/extension/images/icon128.png -------------------------------------------------------------------------------- /testdata/extension/images/icon38-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/testdata/extension/images/icon38-off.png -------------------------------------------------------------------------------- /testdata/extension/images/icon38-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/testdata/extension/images/icon38-on.png -------------------------------------------------------------------------------- /testdata/extension/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/testdata/extension/images/icon48.png -------------------------------------------------------------------------------- /testdata/extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Disable Content-Security-Policy", 3 | "default_locale": "en", 4 | "description": "__MSG_extDescription__", 5 | "version": "1.0.6", 6 | "author": "Phil Grayson", 7 | "homepage_url": "https://github.com/PhilGrayson/chrome-csp-disable", 8 | "manifest_version": 2, 9 | "permissions": [ 10 | "webRequest", 11 | "webRequestBlocking", 12 | "browsingData", 13 | "http://*/*", 14 | "https://*/*" 15 | ], 16 | "background": { 17 | "scripts": ["background.js"], 18 | "persistent": true 19 | }, 20 | "browser_action": { 21 | "default_title": "Content-Security-Policy headers are enabled", 22 | "default_icon": { 23 | "16": "images/icon38-off.png" 24 | } 25 | }, 26 | "icons": { 27 | "48": "images/icon48.png", 28 | "128": "images/icon128.png" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /v2/examples/events/events.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example of using DebugEvents(true) to listen to various events being emitted by 3 | the Remote Chrome Debugger 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "encoding/json" 11 | "flag" 12 | "fmt" 13 | "io/ioutil" 14 | "log" 15 | "net" 16 | "net/http" 17 | "os" 18 | "time" 19 | 20 | "github.com/wirepair/gcd/v2" 21 | "github.com/wirepair/gcd/v2/gcdapi" 22 | ) 23 | 24 | var ( 25 | testListener net.Listener 26 | testPath string 27 | testDir string 28 | testPort string 29 | testServerAddr string 30 | ) 31 | 32 | var testStartupFlags = []string{"--disable-new-tab-first-run", "--no-first-run", "--disable-popup-blocking"} 33 | 34 | func init() { 35 | flag.StringVar(&testPath, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 36 | flag.StringVar(&testDir, "dir", "C:\\temp\\", "user directory") 37 | flag.StringVar(&testPort, "port", "9222", "Debugger port") 38 | } 39 | 40 | func main() { 41 | navigatedCh := make(chan struct{}) 42 | debugger := startGcd() 43 | defer debugger.ExitProcess() 44 | 45 | ctx := context.Background() 46 | target := startTarget(debugger) 47 | 48 | // subscribe to page loaded event 49 | target.Subscribe("Page.loadEventFired", func(targ *gcd.ChromeTarget, payload []byte) { 50 | navigatedCh <- struct{}{} 51 | }) 52 | 53 | // navigate 54 | navigateParams := &gcdapi.PageNavigateParams{Url: testServerAddr + "top.html"} 55 | _, _, _, err := target.Page.NavigateWithParams(ctx, navigateParams) 56 | if err != nil { 57 | log.Fatalf("error: %s\n", err) 58 | } 59 | 60 | // wait for navigation to finish 61 | <-navigatedCh 62 | // get the document node 63 | doc, err := target.DOM.GetDocument(ctx, -1, true) 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | 68 | // request child nodes, this will come in as DOM.setChildNode events 69 | for i := 0; i < 3; i++ { 70 | log.Printf("requesting child nodes...") 71 | _, err = target.DOM.RequestChildNodes(ctx, doc.NodeId, -1, true) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | time.Sleep(1 * time.Second) 76 | } 77 | 78 | target.Subscribe("DOM.setChildNodes", func(targ *gcd.ChromeTarget, payload []byte) { 79 | setNodes := &gcdapi.DOMSetChildNodesEvent{} 80 | err := json.Unmarshal(payload, setNodes) 81 | if err != nil { 82 | log.Fatalf("error decoding setChildNodes") 83 | } 84 | for _, x := range setNodes.Params.Nodes { 85 | if x.ContentDocument != nil { 86 | checkContentDocument(targ, x) 87 | } 88 | for _, v := range x.Children { 89 | if v.ContentDocument != nil { 90 | checkContentDocument(targ, v) 91 | } 92 | } 93 | } 94 | 95 | }) 96 | 97 | // wait for redirect 98 | //time.Sleep(5 * time.Second) 99 | 100 | // get iframe node id 101 | iframe, err := target.DOM.QuerySelector(ctx, doc.NodeId, "#iframe") 102 | if err != nil { 103 | log.Fatalf("error looking for frame") 104 | } 105 | 106 | log.Printf("iframe %d\n", iframe) 107 | time.Sleep(10 * time.Second) 108 | debugger.ExitProcess() 109 | os.RemoveAll(testDir) // remove new profile junk 110 | } 111 | 112 | func checkContentDocument(targ *gcd.ChromeTarget, v *gcdapi.DOMNode) { 113 | ctx := context.Background() 114 | if v.ContentDocument != nil { 115 | iframeDocId := v.ContentDocument.NodeId 116 | targ.DOM.RequestChildNodes(ctx, iframeDocId, -1, true) 117 | //nodes, _ := targ.DOM.QuerySelectorAll(iframeDocId, "div") 118 | log.Printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!got iframe nodes.\n") 119 | } else { 120 | log.Printf("got non iframe\n") 121 | } 122 | } 123 | 124 | func startGcd() *gcd.Gcd { 125 | testDir = testRandomDir() 126 | testPort = testRandomPort() 127 | testServer() // start test web server 128 | debugger := gcd.NewChromeDebugger(gcd.WithFlags(testStartupFlags), gcd.WithEventDebugging()) 129 | debugger.StartProcess(testPath, testDir, testPort) 130 | return debugger 131 | } 132 | 133 | func startTarget(debugger *gcd.Gcd) *gcd.ChromeTarget { 134 | target, err := debugger.NewTab() 135 | if err != nil { 136 | log.Fatalf("error getting new tab: %s\n", err) 137 | } 138 | ctx := context.Background() 139 | target.DOM.Enable(ctx, "false") 140 | target.Console.Enable(ctx) 141 | target.Page.Enable(ctx) 142 | //target.Debugger.Enable() 143 | return target 144 | 145 | } 146 | 147 | func testServer() { 148 | testListener, _ = net.Listen("tcp", ":0") 149 | _, testServerPort, _ := net.SplitHostPort(testListener.Addr().String()) 150 | testServerAddr = fmt.Sprintf("http://localhost:%s/", testServerPort) 151 | go http.Serve(testListener, http.FileServer(http.Dir("webdata/"))) 152 | } 153 | 154 | func testRandomPort() string { 155 | l, err := net.Listen("tcp", ":0") 156 | if err != nil { 157 | log.Fatal(err) 158 | } 159 | _, randPort, _ := net.SplitHostPort(l.Addr().String()) 160 | l.Close() 161 | return randPort 162 | } 163 | 164 | func testRandomDir() string { 165 | dir, err := ioutil.TempDir(testDir, "autogcd") 166 | if err != nil { 167 | log.Fatalf("error getting temp dir: %s\n", err) 168 | } 169 | return dir 170 | } 171 | -------------------------------------------------------------------------------- /v2/examples/events/webdata/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | document modification 4 | 20 | 21 | 22 |
23 |
see ya!
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /v2/examples/events/webdata/target.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | target of redirect 4 | 27 | 28 | 29 |
30 |
i'm a target page
31 |
i'm a target page
32 |
i'm a target page
33 |
i'm a target page
34 |
i'm a target page
35 |
i'm a target page
36 |
i'm a target page
37 |
i'm a target page
38 |
i'm a target page
39 |
i'm a target page
40 |
i'm a target page
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /v2/examples/events/webdata/top.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | frame top 4 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /v2/examples/getdoc/getdoc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "runtime" 8 | 9 | "github.com/wirepair/gcd/v2" 10 | ) 11 | 12 | var path string 13 | var dir string 14 | var port string 15 | 16 | func init() { 17 | switch runtime.GOOS { 18 | case "windows": 19 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 20 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 21 | case "darwin": 22 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 23 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 24 | case "linux": 25 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 26 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 27 | } 28 | 29 | flag.StringVar(&port, "port", "9222", "Debugger port") 30 | } 31 | 32 | func main() { 33 | flag.Parse() 34 | debugger := gcd.NewChromeDebugger() 35 | debugger.StartProcess(path, dir, port) 36 | defer debugger.ExitProcess() 37 | target, err := debugger.NewTab() 38 | if err != nil { 39 | log.Fatalf("error getting new tab: %s\n", err) 40 | } 41 | dom := target.DOM 42 | r, err := dom.GetDocument(context.Background(), -1, true) 43 | if err != nil { 44 | log.Fatalf("error: %s\n", err) 45 | } 46 | 47 | log.Printf("NodeID: %d Node Name: %s, URL: %s\n", r.NodeId, r.NodeName, r.DocumentURL) 48 | } 49 | -------------------------------------------------------------------------------- /v2/examples/loadpage/loadpage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "runtime" 8 | "sync" 9 | 10 | "github.com/wirepair/gcd/v2" 11 | "github.com/wirepair/gcd/v2/gcdapi" 12 | ) 13 | 14 | var path string 15 | var dir string 16 | var port string 17 | 18 | func init() { 19 | switch runtime.GOOS { 20 | case "windows": 21 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 22 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 23 | case "darwin": 24 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 25 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 26 | case "linux": 27 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 28 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 29 | } 30 | 31 | flag.StringVar(&port, "port", "9222", "Debugger port") 32 | } 33 | 34 | func main() { 35 | var wg sync.WaitGroup 36 | wg.Add(1) 37 | flag.Parse() 38 | debugger := gcd.NewChromeDebugger() 39 | 40 | // start process, specify a tmp profile path so we get a fresh profiled browser 41 | // set port 9222 as the debug port 42 | debugger.StartProcess(path, dir, port) 43 | defer debugger.ExitProcess() // exit when done 44 | 45 | ctx := context.Background() 46 | target, err := debugger.NewTab() 47 | if err != nil { 48 | log.Fatalf("error opening new tab: %s\n", err) 49 | } 50 | 51 | //subscribe to page load 52 | target.Subscribe("Page.loadEventFired", func(targ *gcd.ChromeTarget, v []byte) { 53 | doc, err := target.DOM.GetDocument(ctx, -1, true) 54 | if err == nil { 55 | log.Printf("%s\n", doc.DocumentURL) 56 | } 57 | wg.Done() // page loaded, we can exit now 58 | // if you wanted to inspect the full response data, you could do that here 59 | }) 60 | 61 | // get the Page API and enable it 62 | if _, err := target.Page.Enable(ctx); err != nil { 63 | log.Fatalf("error getting page: %s\n", err) 64 | } 65 | 66 | navigateParams := &gcdapi.PageNavigateParams{Url: "http://www.veracode.com"} 67 | ret, _, _, err := target.Page.NavigateWithParams(ctx, navigateParams) // navigate 68 | if err != nil { 69 | log.Fatalf("Error navigating: %s\n", err) 70 | } 71 | 72 | log.Printf("ret: %#v\n", ret) 73 | wg.Wait() // wait for page load 74 | debugger.CloseTab(target) 75 | } 76 | -------------------------------------------------------------------------------- /v2/examples/screenshot/screenshot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net/url" 10 | "os" 11 | "runtime" 12 | "sync" 13 | "time" 14 | 15 | "github.com/wirepair/gcd/v2" 16 | "github.com/wirepair/gcd/v2/gcdapi" 17 | ) 18 | 19 | const ( 20 | numTabs = 5 21 | ) 22 | 23 | var debugger *gcd.Gcd 24 | var wg sync.WaitGroup 25 | 26 | var path string 27 | var dir string 28 | var port string 29 | 30 | func init() { 31 | switch runtime.GOOS { 32 | case "windows": 33 | flag.StringVar(&path, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to chrome") 34 | flag.StringVar(&dir, "dir", "C:\\temp\\", "user directory") 35 | case "darwin": 36 | flag.StringVar(&path, "chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "path to chrome") 37 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 38 | case "linux": 39 | flag.StringVar(&path, "chrome", "/usr/bin/chromium-browser", "path to chrome") 40 | flag.StringVar(&dir, "dir", "/tmp/", "user directory") 41 | } 42 | 43 | flag.StringVar(&port, "port", "9222", "Debugger port") 44 | } 45 | 46 | func main() { 47 | var err error 48 | urls := []string{"http://www.google.com", "http://www.veracode.com", "http://www.microsoft.com", "http://bbc.co.uk", "http://www.reddit.com/r/golang"} 49 | ctx := context.Background() 50 | flag.Parse() 51 | 52 | debugger = gcd.NewChromeDebugger() 53 | debugger.StartProcess(path, dir, port) 54 | defer debugger.ExitProcess() 55 | 56 | targets := make([]*gcd.ChromeTarget, numTabs) 57 | 58 | for i := 0; i < numTabs; i++ { 59 | wg.Add(1) 60 | targets[i], err = debugger.NewTab() 61 | if err != nil { 62 | log.Fatalf("error getting targets: %s", err) 63 | } 64 | page := targets[i].Page 65 | page.Enable(ctx) 66 | targets[i].Subscribe("Page.loadEventFired", pageLoaded) 67 | // navigate 68 | navigateParams := &gcdapi.PageNavigateParams{Url: urls[i]} 69 | _, _, _, err := page.NavigateWithParams(ctx, navigateParams) 70 | if err != nil { 71 | log.Fatalf("error: %s\n", err) 72 | } 73 | } 74 | wg.Wait() 75 | for i := 0; i < numTabs; i++ { 76 | takeScreenShot(targets[i]) 77 | } 78 | } 79 | 80 | func pageLoaded(target *gcd.ChromeTarget, event []byte) { 81 | target.Unsubscribe("Page.loadEventFired") 82 | wg.Done() 83 | } 84 | 85 | func takeScreenShot(target *gcd.ChromeTarget) { 86 | ctx := context.Background() 87 | dom := target.DOM 88 | page := target.Page 89 | doc, err := dom.GetDocument(ctx, -1, true) 90 | if err != nil { 91 | fmt.Printf("error getting doc: %s\n", err) 92 | return 93 | } 94 | 95 | debugger.ActivateTab(target) 96 | time.Sleep(1 * time.Second) // give it a sec to paint 97 | u, urlErr := url.Parse(doc.DocumentURL) 98 | if urlErr != nil { 99 | fmt.Printf("error parsing url: %s\n", urlErr) 100 | return 101 | } 102 | 103 | fmt.Printf("Taking screen shot of: %s\n", u.Host) 104 | screenShotParams := &gcdapi.PageCaptureScreenshotParams{Format: "png", FromSurface: true} 105 | img, errCap := page.CaptureScreenshotWithParams(ctx, screenShotParams) 106 | if errCap != nil { 107 | fmt.Printf("error getting doc: %s\n", errCap) 108 | return 109 | } 110 | 111 | imgBytes, errDecode := base64.StdEncoding.DecodeString(img) 112 | if errDecode != nil { 113 | fmt.Printf("error decoding image: %s\n", errDecode) 114 | return 115 | } 116 | 117 | f, errFile := os.Create(u.Host + ".png") 118 | defer f.Close() 119 | if errFile != nil { 120 | fmt.Printf("error creating image file: %s\n", errFile) 121 | return 122 | } 123 | f.Write(imgBytes) 124 | debugger.CloseTab(target) 125 | } 126 | -------------------------------------------------------------------------------- /v2/gcdapi/autofill.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Autofill functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // No Description. 13 | type AutofillCreditCard struct { 14 | Number string `json:"number"` // 16-digit credit card number. 15 | Name string `json:"name"` // Name of the credit card owner. 16 | ExpiryMonth string `json:"expiryMonth"` // 2-digit expiry month. 17 | ExpiryYear string `json:"expiryYear"` // 4-digit expiry year. 18 | Cvc string `json:"cvc"` // 3-digit card verification code. 19 | } 20 | 21 | // No Description. 22 | type AutofillAddressField struct { 23 | Name string `json:"name"` // address field name, for example GIVEN_NAME. 24 | Value string `json:"value"` // address field value, for example Jon Doe. 25 | } 26 | 27 | // A list of address fields. 28 | type AutofillAddressFields struct { 29 | Fields []*AutofillAddressField `json:"fields"` // 30 | } 31 | 32 | // No Description. 33 | type AutofillAddress struct { 34 | Fields []*AutofillAddressField `json:"fields"` // fields and values defining an address. 35 | } 36 | 37 | // Defines how an address can be displayed like in chrome://settings/addresses. Address UI is a two dimensional array, each inner array is an "address information line", and when rendered in a UI surface should be displayed as such. The following address UI for instance: [[{name: "GIVE_NAME", value: "Jon"}, {name: "FAMILY_NAME", value: "Doe"}], [{name: "CITY", value: "Munich"}, {name: "ZIP", value: "81456"}]] should allow the receiver to render: Jon Doe Munich 81456 38 | type AutofillAddressUI struct { 39 | AddressFields []*AutofillAddressFields `json:"addressFields"` // A two dimension array containing the representation of values from an address profile. 40 | } 41 | 42 | // No Description. 43 | type AutofillFilledField struct { 44 | HtmlType string `json:"htmlType"` // The type of the field, e.g text, password etc. 45 | Id string `json:"id"` // the html id 46 | Name string `json:"name"` // the html name 47 | Value string `json:"value"` // the field value 48 | AutofillType string `json:"autofillType"` // The actual field type, e.g FAMILY_NAME 49 | FillingStrategy string `json:"fillingStrategy"` // The filling strategy enum values: autocompleteAttribute, autofillInferred 50 | FrameId string `json:"frameId"` // The frame the field belongs to 51 | FieldId int `json:"fieldId"` // The form field's DOM node 52 | } 53 | 54 | // Emitted when an address form is filled. 55 | type AutofillAddressFormFilledEvent struct { 56 | Method string `json:"method"` 57 | Params struct { 58 | FilledFields []*AutofillFilledField `json:"filledFields"` // Information about the fields that were filled 59 | AddressUi *AutofillAddressUI `json:"addressUi"` // An UI representation of the address used to fill the form. Consists of a 2D array where each child represents an address/profile line. 60 | } `json:"Params,omitempty"` 61 | } 62 | 63 | type Autofill struct { 64 | target gcdmessage.ChromeTargeter 65 | } 66 | 67 | func NewAutofill(target gcdmessage.ChromeTargeter) *Autofill { 68 | c := &Autofill{target: target} 69 | return c 70 | } 71 | 72 | type AutofillTriggerParams struct { 73 | // Identifies a field that serves as an anchor for autofill. 74 | FieldId int `json:"fieldId"` 75 | // Identifies the frame that field belongs to. 76 | FrameId string `json:"frameId,omitempty"` 77 | // Credit card information to fill out the form. Credit card data is not saved. 78 | Card *AutofillCreditCard `json:"card"` 79 | } 80 | 81 | // TriggerWithParams - Trigger autofill on a form identified by the fieldId. If the field and related form cannot be autofilled, returns an error. 82 | func (c *Autofill) TriggerWithParams(ctx context.Context, v *AutofillTriggerParams) (*gcdmessage.ChromeResponse, error) { 83 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Autofill.trigger", Params: v}) 84 | } 85 | 86 | // Trigger - Trigger autofill on a form identified by the fieldId. If the field and related form cannot be autofilled, returns an error. 87 | // fieldId - Identifies a field that serves as an anchor for autofill. 88 | // frameId - Identifies the frame that field belongs to. 89 | // card - Credit card information to fill out the form. Credit card data is not saved. 90 | func (c *Autofill) Trigger(ctx context.Context, fieldId int, frameId string, card *AutofillCreditCard) (*gcdmessage.ChromeResponse, error) { 91 | var v AutofillTriggerParams 92 | v.FieldId = fieldId 93 | v.FrameId = frameId 94 | v.Card = card 95 | return c.TriggerWithParams(ctx, &v) 96 | } 97 | 98 | type AutofillSetAddressesParams struct { 99 | // 100 | Addresses []*AutofillAddress `json:"addresses"` 101 | } 102 | 103 | // SetAddressesWithParams - Set addresses so that developers can verify their forms implementation. 104 | func (c *Autofill) SetAddressesWithParams(ctx context.Context, v *AutofillSetAddressesParams) (*gcdmessage.ChromeResponse, error) { 105 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Autofill.setAddresses", Params: v}) 106 | } 107 | 108 | // SetAddresses - Set addresses so that developers can verify their forms implementation. 109 | // addresses - 110 | func (c *Autofill) SetAddresses(ctx context.Context, addresses []*AutofillAddress) (*gcdmessage.ChromeResponse, error) { 111 | var v AutofillSetAddressesParams 112 | v.Addresses = addresses 113 | return c.SetAddressesWithParams(ctx, &v) 114 | } 115 | 116 | // Disables autofill domain notifications. 117 | func (c *Autofill) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 118 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Autofill.disable"}) 119 | } 120 | 121 | // Enables autofill domain notifications. 122 | func (c *Autofill) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 123 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Autofill.enable"}) 124 | } 125 | -------------------------------------------------------------------------------- /v2/gcdapi/bluetoothemulation.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains BluetoothEmulation functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Stores the manufacturer data 13 | type BluetoothEmulationManufacturerData struct { 14 | Key int `json:"key"` // Company identifier https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml https://usb.org/developers 15 | Data string `json:"data"` // Manufacturer-specific data (Encoded as a base64 string when passed over JSON) 16 | } 17 | 18 | // Stores the byte data of the advertisement packet sent by a Bluetooth device. 19 | type BluetoothEmulationScanRecord struct { 20 | Name string `json:"name,omitempty"` // 21 | Uuids []string `json:"uuids,omitempty"` // 22 | Appearance int `json:"appearance,omitempty"` // Stores the external appearance description of the device. 23 | TxPower int `json:"txPower,omitempty"` // Stores the transmission power of a broadcasting device. 24 | ManufacturerData []*BluetoothEmulationManufacturerData `json:"manufacturerData,omitempty"` // Key is the company identifier and the value is an array of bytes of manufacturer specific data. 25 | } 26 | 27 | // Stores the advertisement packet information that is sent by a Bluetooth device. 28 | type BluetoothEmulationScanEntry struct { 29 | DeviceAddress string `json:"deviceAddress"` // 30 | Rssi int `json:"rssi"` // 31 | ScanRecord *BluetoothEmulationScanRecord `json:"scanRecord"` // 32 | } 33 | 34 | type BluetoothEmulation struct { 35 | target gcdmessage.ChromeTargeter 36 | } 37 | 38 | func NewBluetoothEmulation(target gcdmessage.ChromeTargeter) *BluetoothEmulation { 39 | c := &BluetoothEmulation{target: target} 40 | return c 41 | } 42 | 43 | type BluetoothEmulationEnableParams struct { 44 | // State of the simulated central. enum values: absent, powered-off, powered-on 45 | State string `json:"state"` 46 | } 47 | 48 | // EnableWithParams - Enable the BluetoothEmulation domain. 49 | func (c *BluetoothEmulation) EnableWithParams(ctx context.Context, v *BluetoothEmulationEnableParams) (*gcdmessage.ChromeResponse, error) { 50 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "BluetoothEmulation.enable", Params: v}) 51 | } 52 | 53 | // Enable - Enable the BluetoothEmulation domain. 54 | // state - State of the simulated central. enum values: absent, powered-off, powered-on 55 | func (c *BluetoothEmulation) Enable(ctx context.Context, state string) (*gcdmessage.ChromeResponse, error) { 56 | var v BluetoothEmulationEnableParams 57 | v.State = state 58 | return c.EnableWithParams(ctx, &v) 59 | } 60 | 61 | // Disable the BluetoothEmulation domain. 62 | func (c *BluetoothEmulation) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 63 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "BluetoothEmulation.disable"}) 64 | } 65 | 66 | type BluetoothEmulationSimulatePreconnectedPeripheralParams struct { 67 | // 68 | Address string `json:"address"` 69 | // 70 | Name string `json:"name"` 71 | // 72 | ManufacturerData []*BluetoothEmulationManufacturerData `json:"manufacturerData"` 73 | // 74 | KnownServiceUuids []string `json:"knownServiceUuids"` 75 | } 76 | 77 | // SimulatePreconnectedPeripheralWithParams - Simulates a peripheral with |address|, |name| and |knownServiceUuids| that has already been connected to the system. 78 | func (c *BluetoothEmulation) SimulatePreconnectedPeripheralWithParams(ctx context.Context, v *BluetoothEmulationSimulatePreconnectedPeripheralParams) (*gcdmessage.ChromeResponse, error) { 79 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "BluetoothEmulation.simulatePreconnectedPeripheral", Params: v}) 80 | } 81 | 82 | // SimulatePreconnectedPeripheral - Simulates a peripheral with |address|, |name| and |knownServiceUuids| that has already been connected to the system. 83 | // address - 84 | // name - 85 | // manufacturerData - 86 | // knownServiceUuids - 87 | func (c *BluetoothEmulation) SimulatePreconnectedPeripheral(ctx context.Context, address string, name string, manufacturerData []*BluetoothEmulationManufacturerData, knownServiceUuids []string) (*gcdmessage.ChromeResponse, error) { 88 | var v BluetoothEmulationSimulatePreconnectedPeripheralParams 89 | v.Address = address 90 | v.Name = name 91 | v.ManufacturerData = manufacturerData 92 | v.KnownServiceUuids = knownServiceUuids 93 | return c.SimulatePreconnectedPeripheralWithParams(ctx, &v) 94 | } 95 | 96 | type BluetoothEmulationSimulateAdvertisementParams struct { 97 | // 98 | Entry *BluetoothEmulationScanEntry `json:"entry"` 99 | } 100 | 101 | // SimulateAdvertisementWithParams - Simulates an advertisement packet described in |entry| being received by the central. 102 | func (c *BluetoothEmulation) SimulateAdvertisementWithParams(ctx context.Context, v *BluetoothEmulationSimulateAdvertisementParams) (*gcdmessage.ChromeResponse, error) { 103 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "BluetoothEmulation.simulateAdvertisement", Params: v}) 104 | } 105 | 106 | // SimulateAdvertisement - Simulates an advertisement packet described in |entry| being received by the central. 107 | // entry - 108 | func (c *BluetoothEmulation) SimulateAdvertisement(ctx context.Context, entry *BluetoothEmulationScanEntry) (*gcdmessage.ChromeResponse, error) { 109 | var v BluetoothEmulationSimulateAdvertisementParams 110 | v.Entry = entry 111 | return c.SimulateAdvertisementWithParams(ctx, &v) 112 | } 113 | -------------------------------------------------------------------------------- /v2/gcdapi/cast.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Cast functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // No Description. 13 | type CastSink struct { 14 | Name string `json:"name"` // 15 | Id string `json:"id"` // 16 | Session string `json:"session,omitempty"` // Text describing the current session. Present only if there is an active session on the sink. 17 | } 18 | 19 | // This is fired whenever the list of available sinks changes. A sink is a device or a software surface that you can cast to. 20 | type CastSinksUpdatedEvent struct { 21 | Method string `json:"method"` 22 | Params struct { 23 | Sinks []*CastSink `json:"sinks"` // 24 | } `json:"Params,omitempty"` 25 | } 26 | 27 | // This is fired whenever the outstanding issue/error message changes. |issueMessage| is empty if there is no issue. 28 | type CastIssueUpdatedEvent struct { 29 | Method string `json:"method"` 30 | Params struct { 31 | IssueMessage string `json:"issueMessage"` // 32 | } `json:"Params,omitempty"` 33 | } 34 | 35 | type Cast struct { 36 | target gcdmessage.ChromeTargeter 37 | } 38 | 39 | func NewCast(target gcdmessage.ChromeTargeter) *Cast { 40 | c := &Cast{target: target} 41 | return c 42 | } 43 | 44 | type CastEnableParams struct { 45 | // 46 | PresentationUrl string `json:"presentationUrl,omitempty"` 47 | } 48 | 49 | // EnableWithParams - Starts observing for sinks that can be used for tab mirroring, and if set, sinks compatible with |presentationUrl| as well. When sinks are found, a |sinksUpdated| event is fired. Also starts observing for issue messages. When an issue is added or removed, an |issueUpdated| event is fired. 50 | func (c *Cast) EnableWithParams(ctx context.Context, v *CastEnableParams) (*gcdmessage.ChromeResponse, error) { 51 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.enable", Params: v}) 52 | } 53 | 54 | // Enable - Starts observing for sinks that can be used for tab mirroring, and if set, sinks compatible with |presentationUrl| as well. When sinks are found, a |sinksUpdated| event is fired. Also starts observing for issue messages. When an issue is added or removed, an |issueUpdated| event is fired. 55 | // presentationUrl - 56 | func (c *Cast) Enable(ctx context.Context, presentationUrl string) (*gcdmessage.ChromeResponse, error) { 57 | var v CastEnableParams 58 | v.PresentationUrl = presentationUrl 59 | return c.EnableWithParams(ctx, &v) 60 | } 61 | 62 | // Stops observing for sinks and issues. 63 | func (c *Cast) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 64 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.disable"}) 65 | } 66 | 67 | type CastSetSinkToUseParams struct { 68 | // 69 | SinkName string `json:"sinkName"` 70 | } 71 | 72 | // SetSinkToUseWithParams - Sets a sink to be used when the web page requests the browser to choose a sink via Presentation API, Remote Playback API, or Cast SDK. 73 | func (c *Cast) SetSinkToUseWithParams(ctx context.Context, v *CastSetSinkToUseParams) (*gcdmessage.ChromeResponse, error) { 74 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.setSinkToUse", Params: v}) 75 | } 76 | 77 | // SetSinkToUse - Sets a sink to be used when the web page requests the browser to choose a sink via Presentation API, Remote Playback API, or Cast SDK. 78 | // sinkName - 79 | func (c *Cast) SetSinkToUse(ctx context.Context, sinkName string) (*gcdmessage.ChromeResponse, error) { 80 | var v CastSetSinkToUseParams 81 | v.SinkName = sinkName 82 | return c.SetSinkToUseWithParams(ctx, &v) 83 | } 84 | 85 | type CastStartDesktopMirroringParams struct { 86 | // 87 | SinkName string `json:"sinkName"` 88 | } 89 | 90 | // StartDesktopMirroringWithParams - Starts mirroring the desktop to the sink. 91 | func (c *Cast) StartDesktopMirroringWithParams(ctx context.Context, v *CastStartDesktopMirroringParams) (*gcdmessage.ChromeResponse, error) { 92 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.startDesktopMirroring", Params: v}) 93 | } 94 | 95 | // StartDesktopMirroring - Starts mirroring the desktop to the sink. 96 | // sinkName - 97 | func (c *Cast) StartDesktopMirroring(ctx context.Context, sinkName string) (*gcdmessage.ChromeResponse, error) { 98 | var v CastStartDesktopMirroringParams 99 | v.SinkName = sinkName 100 | return c.StartDesktopMirroringWithParams(ctx, &v) 101 | } 102 | 103 | type CastStartTabMirroringParams struct { 104 | // 105 | SinkName string `json:"sinkName"` 106 | } 107 | 108 | // StartTabMirroringWithParams - Starts mirroring the tab to the sink. 109 | func (c *Cast) StartTabMirroringWithParams(ctx context.Context, v *CastStartTabMirroringParams) (*gcdmessage.ChromeResponse, error) { 110 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.startTabMirroring", Params: v}) 111 | } 112 | 113 | // StartTabMirroring - Starts mirroring the tab to the sink. 114 | // sinkName - 115 | func (c *Cast) StartTabMirroring(ctx context.Context, sinkName string) (*gcdmessage.ChromeResponse, error) { 116 | var v CastStartTabMirroringParams 117 | v.SinkName = sinkName 118 | return c.StartTabMirroringWithParams(ctx, &v) 119 | } 120 | 121 | type CastStopCastingParams struct { 122 | // 123 | SinkName string `json:"sinkName"` 124 | } 125 | 126 | // StopCastingWithParams - Stops the active Cast session on the sink. 127 | func (c *Cast) StopCastingWithParams(ctx context.Context, v *CastStopCastingParams) (*gcdmessage.ChromeResponse, error) { 128 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Cast.stopCasting", Params: v}) 129 | } 130 | 131 | // StopCasting - Stops the active Cast session on the sink. 132 | // sinkName - 133 | func (c *Cast) StopCasting(ctx context.Context, sinkName string) (*gcdmessage.ChromeResponse, error) { 134 | var v CastStopCastingParams 135 | v.SinkName = sinkName 136 | return c.StopCastingWithParams(ctx, &v) 137 | } 138 | -------------------------------------------------------------------------------- /v2/gcdapi/console.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Console functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Console message. 13 | type ConsoleConsoleMessage struct { 14 | Source string `json:"source"` // Message source. 15 | Level string `json:"level"` // Message severity. 16 | Text string `json:"text"` // Message text. 17 | Url string `json:"url,omitempty"` // URL of the message origin. 18 | Line int `json:"line,omitempty"` // Line number in the resource that generated this message (1-based). 19 | Column int `json:"column,omitempty"` // Column number in the resource that generated this message (1-based). 20 | } 21 | 22 | // Issued when new console message is added. 23 | type ConsoleMessageAddedEvent struct { 24 | Method string `json:"method"` 25 | Params struct { 26 | Message *ConsoleConsoleMessage `json:"message"` // Console message that has been added. 27 | } `json:"Params,omitempty"` 28 | } 29 | 30 | type Console struct { 31 | target gcdmessage.ChromeTargeter 32 | } 33 | 34 | func NewConsole(target gcdmessage.ChromeTargeter) *Console { 35 | c := &Console{target: target} 36 | return c 37 | } 38 | 39 | // Does nothing. 40 | func (c *Console) ClearMessages(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 41 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.clearMessages"}) 42 | } 43 | 44 | // Disables console domain, prevents further console messages from being reported to the client. 45 | func (c *Console) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 46 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.disable"}) 47 | } 48 | 49 | // Enables console domain, sends the messages collected so far to the client by means of the `messageAdded` notification. 50 | func (c *Console) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 51 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Console.enable"}) 52 | } 53 | -------------------------------------------------------------------------------- /v2/gcdapi/database.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Database functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Database object. 13 | type DatabaseDatabase struct { 14 | Id string `json:"id"` // Database ID. 15 | Domain string `json:"domain"` // Database domain. 16 | Name string `json:"name"` // Database name. 17 | Version string `json:"version"` // Database version. 18 | } 19 | 20 | // Database error. 21 | type DatabaseError struct { 22 | Message string `json:"message"` // Error message. 23 | Code int `json:"code"` // Error code. 24 | } 25 | 26 | // 27 | type DatabaseAddDatabaseEvent struct { 28 | Method string `json:"method"` 29 | Params struct { 30 | Database *DatabaseDatabase `json:"database"` // 31 | } `json:"Params,omitempty"` 32 | } 33 | 34 | type Database struct { 35 | target gcdmessage.ChromeTargeter 36 | } 37 | 38 | func NewDatabase(target gcdmessage.ChromeTargeter) *Database { 39 | c := &Database{target: target} 40 | return c 41 | } 42 | 43 | // Disables database tracking, prevents database events from being sent to the client. 44 | func (c *Database) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 45 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.disable"}) 46 | } 47 | 48 | // Enables database tracking, database events will now be delivered to the client. 49 | func (c *Database) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 50 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.enable"}) 51 | } 52 | 53 | type DatabaseExecuteSQLParams struct { 54 | // 55 | DatabaseId string `json:"databaseId"` 56 | // 57 | Query string `json:"query"` 58 | } 59 | 60 | // ExecuteSQLWithParams - 61 | // Returns - columnNames - values - sqlError - 62 | func (c *Database) ExecuteSQLWithParams(ctx context.Context, v *DatabaseExecuteSQLParams) ([]string, []interface{}, *DatabaseError, error) { 63 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.executeSQL", Params: v}) 64 | if err != nil { 65 | return nil, nil, nil, err 66 | } 67 | 68 | var chromeData struct { 69 | gcdmessage.ChromeErrorResponse 70 | Result struct { 71 | ColumnNames []string 72 | Values []interface{} 73 | SqlError *DatabaseError 74 | } 75 | } 76 | 77 | if resp == nil { 78 | return nil, nil, nil, &gcdmessage.ChromeEmptyResponseErr{} 79 | } 80 | 81 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 82 | return nil, nil, nil, err 83 | } 84 | 85 | if chromeData.Error != nil { 86 | return nil, nil, nil, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 87 | } 88 | 89 | return chromeData.Result.ColumnNames, chromeData.Result.Values, chromeData.Result.SqlError, nil 90 | } 91 | 92 | // ExecuteSQL - 93 | // databaseId - 94 | // query - 95 | // Returns - columnNames - values - sqlError - 96 | func (c *Database) ExecuteSQL(ctx context.Context, databaseId string, query string) ([]string, []interface{}, *DatabaseError, error) { 97 | var v DatabaseExecuteSQLParams 98 | v.DatabaseId = databaseId 99 | v.Query = query 100 | return c.ExecuteSQLWithParams(ctx, &v) 101 | } 102 | 103 | type DatabaseGetDatabaseTableNamesParams struct { 104 | // 105 | DatabaseId string `json:"databaseId"` 106 | } 107 | 108 | // GetDatabaseTableNamesWithParams - 109 | // Returns - tableNames - 110 | func (c *Database) GetDatabaseTableNamesWithParams(ctx context.Context, v *DatabaseGetDatabaseTableNamesParams) ([]string, error) { 111 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Database.getDatabaseTableNames", Params: v}) 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | var chromeData struct { 117 | gcdmessage.ChromeErrorResponse 118 | Result struct { 119 | TableNames []string 120 | } 121 | } 122 | 123 | if resp == nil { 124 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 125 | } 126 | 127 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 128 | return nil, err 129 | } 130 | 131 | if chromeData.Error != nil { 132 | return nil, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 133 | } 134 | 135 | return chromeData.Result.TableNames, nil 136 | } 137 | 138 | // GetDatabaseTableNames - 139 | // databaseId - 140 | // Returns - tableNames - 141 | func (c *Database) GetDatabaseTableNames(ctx context.Context, databaseId string) ([]string, error) { 142 | var v DatabaseGetDatabaseTableNamesParams 143 | v.DatabaseId = databaseId 144 | return c.GetDatabaseTableNamesWithParams(ctx, &v) 145 | } 146 | -------------------------------------------------------------------------------- /v2/gcdapi/deviceaccess.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains DeviceAccess functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Device information displayed in a user prompt to select a device. 13 | type DeviceAccessPromptDevice struct { 14 | Id string `json:"id"` // 15 | Name string `json:"name"` // Display name as it appears in a device request user prompt. 16 | } 17 | 18 | // A device request opened a user prompt to select a device. Respond with the selectPrompt or cancelPrompt command. 19 | type DeviceAccessDeviceRequestPromptedEvent struct { 20 | Method string `json:"method"` 21 | Params struct { 22 | Id string `json:"id"` // 23 | Devices []*DeviceAccessPromptDevice `json:"devices"` // 24 | } `json:"Params,omitempty"` 25 | } 26 | 27 | type DeviceAccess struct { 28 | target gcdmessage.ChromeTargeter 29 | } 30 | 31 | func NewDeviceAccess(target gcdmessage.ChromeTargeter) *DeviceAccess { 32 | c := &DeviceAccess{target: target} 33 | return c 34 | } 35 | 36 | // Enable events in this domain. 37 | func (c *DeviceAccess) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 38 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceAccess.enable"}) 39 | } 40 | 41 | // Disable events in this domain. 42 | func (c *DeviceAccess) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 43 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceAccess.disable"}) 44 | } 45 | 46 | type DeviceAccessSelectPromptParams struct { 47 | // 48 | Id string `json:"id"` 49 | // 50 | DeviceId string `json:"deviceId"` 51 | } 52 | 53 | // SelectPromptWithParams - Select a device in response to a DeviceAccess.deviceRequestPrompted event. 54 | func (c *DeviceAccess) SelectPromptWithParams(ctx context.Context, v *DeviceAccessSelectPromptParams) (*gcdmessage.ChromeResponse, error) { 55 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceAccess.selectPrompt", Params: v}) 56 | } 57 | 58 | // SelectPrompt - Select a device in response to a DeviceAccess.deviceRequestPrompted event. 59 | // id - 60 | // deviceId - 61 | func (c *DeviceAccess) SelectPrompt(ctx context.Context, id string, deviceId string) (*gcdmessage.ChromeResponse, error) { 62 | var v DeviceAccessSelectPromptParams 63 | v.Id = id 64 | v.DeviceId = deviceId 65 | return c.SelectPromptWithParams(ctx, &v) 66 | } 67 | 68 | type DeviceAccessCancelPromptParams struct { 69 | // 70 | Id string `json:"id"` 71 | } 72 | 73 | // CancelPromptWithParams - Cancel a prompt in response to a DeviceAccess.deviceRequestPrompted event. 74 | func (c *DeviceAccess) CancelPromptWithParams(ctx context.Context, v *DeviceAccessCancelPromptParams) (*gcdmessage.ChromeResponse, error) { 75 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceAccess.cancelPrompt", Params: v}) 76 | } 77 | 78 | // CancelPrompt - Cancel a prompt in response to a DeviceAccess.deviceRequestPrompted event. 79 | // id - 80 | func (c *DeviceAccess) CancelPrompt(ctx context.Context, id string) (*gcdmessage.ChromeResponse, error) { 81 | var v DeviceAccessCancelPromptParams 82 | v.Id = id 83 | return c.CancelPromptWithParams(ctx, &v) 84 | } 85 | -------------------------------------------------------------------------------- /v2/gcdapi/deviceorientation.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains DeviceOrientation functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | type DeviceOrientation struct { 13 | target gcdmessage.ChromeTargeter 14 | } 15 | 16 | func NewDeviceOrientation(target gcdmessage.ChromeTargeter) *DeviceOrientation { 17 | c := &DeviceOrientation{target: target} 18 | return c 19 | } 20 | 21 | // Clears the overridden Device Orientation. 22 | func (c *DeviceOrientation) ClearDeviceOrientationOverride(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 23 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceOrientation.clearDeviceOrientationOverride"}) 24 | } 25 | 26 | type DeviceOrientationSetDeviceOrientationOverrideParams struct { 27 | // Mock alpha 28 | Alpha float64 `json:"alpha"` 29 | // Mock beta 30 | Beta float64 `json:"beta"` 31 | // Mock gamma 32 | Gamma float64 `json:"gamma"` 33 | } 34 | 35 | // SetDeviceOrientationOverrideWithParams - Overrides the Device Orientation. 36 | func (c *DeviceOrientation) SetDeviceOrientationOverrideWithParams(ctx context.Context, v *DeviceOrientationSetDeviceOrientationOverrideParams) (*gcdmessage.ChromeResponse, error) { 37 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "DeviceOrientation.setDeviceOrientationOverride", Params: v}) 38 | } 39 | 40 | // SetDeviceOrientationOverride - Overrides the Device Orientation. 41 | // alpha - Mock alpha 42 | // beta - Mock beta 43 | // gamma - Mock gamma 44 | func (c *DeviceOrientation) SetDeviceOrientationOverride(ctx context.Context, alpha float64, beta float64, gamma float64) (*gcdmessage.ChromeResponse, error) { 45 | var v DeviceOrientationSetDeviceOrientationOverrideParams 46 | v.Alpha = alpha 47 | v.Beta = beta 48 | v.Gamma = gamma 49 | return c.SetDeviceOrientationOverrideWithParams(ctx, &v) 50 | } 51 | -------------------------------------------------------------------------------- /v2/gcdapi/eventbreakpoints.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains EventBreakpoints functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | type EventBreakpoints struct { 13 | target gcdmessage.ChromeTargeter 14 | } 15 | 16 | func NewEventBreakpoints(target gcdmessage.ChromeTargeter) *EventBreakpoints { 17 | c := &EventBreakpoints{target: target} 18 | return c 19 | } 20 | 21 | type EventBreakpointsSetInstrumentationBreakpointParams struct { 22 | // Instrumentation name to stop on. 23 | EventName string `json:"eventName"` 24 | } 25 | 26 | // SetInstrumentationBreakpointWithParams - Sets breakpoint on particular native event. 27 | func (c *EventBreakpoints) SetInstrumentationBreakpointWithParams(ctx context.Context, v *EventBreakpointsSetInstrumentationBreakpointParams) (*gcdmessage.ChromeResponse, error) { 28 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "EventBreakpoints.setInstrumentationBreakpoint", Params: v}) 29 | } 30 | 31 | // SetInstrumentationBreakpoint - Sets breakpoint on particular native event. 32 | // eventName - Instrumentation name to stop on. 33 | func (c *EventBreakpoints) SetInstrumentationBreakpoint(ctx context.Context, eventName string) (*gcdmessage.ChromeResponse, error) { 34 | var v EventBreakpointsSetInstrumentationBreakpointParams 35 | v.EventName = eventName 36 | return c.SetInstrumentationBreakpointWithParams(ctx, &v) 37 | } 38 | 39 | type EventBreakpointsRemoveInstrumentationBreakpointParams struct { 40 | // Instrumentation name to stop on. 41 | EventName string `json:"eventName"` 42 | } 43 | 44 | // RemoveInstrumentationBreakpointWithParams - Removes breakpoint on particular native event. 45 | func (c *EventBreakpoints) RemoveInstrumentationBreakpointWithParams(ctx context.Context, v *EventBreakpointsRemoveInstrumentationBreakpointParams) (*gcdmessage.ChromeResponse, error) { 46 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "EventBreakpoints.removeInstrumentationBreakpoint", Params: v}) 47 | } 48 | 49 | // RemoveInstrumentationBreakpoint - Removes breakpoint on particular native event. 50 | // eventName - Instrumentation name to stop on. 51 | func (c *EventBreakpoints) RemoveInstrumentationBreakpoint(ctx context.Context, eventName string) (*gcdmessage.ChromeResponse, error) { 52 | var v EventBreakpointsRemoveInstrumentationBreakpointParams 53 | v.EventName = eventName 54 | return c.RemoveInstrumentationBreakpointWithParams(ctx, &v) 55 | } 56 | 57 | // Removes all breakpoints 58 | func (c *EventBreakpoints) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 59 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "EventBreakpoints.disable"}) 60 | } 61 | -------------------------------------------------------------------------------- /v2/gcdapi/filesystem.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains FileSystem functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // No Description. 13 | type FileSystemFile struct { 14 | Name string `json:"name"` // 15 | LastModified float64 `json:"lastModified"` // Timestamp 16 | Size float64 `json:"size"` // Size in bytes 17 | Type string `json:"type"` // 18 | } 19 | 20 | // No Description. 21 | type FileSystemDirectory struct { 22 | Name string `json:"name"` // 23 | NestedDirectories []string `json:"nestedDirectories"` // 24 | NestedFiles []*FileSystemFile `json:"nestedFiles"` // Files that are directly nested under this directory. 25 | } 26 | 27 | // No Description. 28 | type FileSystemBucketFileSystemLocator struct { 29 | StorageKey string `json:"storageKey"` // Storage key 30 | BucketName string `json:"bucketName,omitempty"` // Bucket name. Not passing a `bucketName` will retrieve the default Bucket. (https://developer.mozilla.org/en-US/docs/Web/API/Storage_API#storage_buckets) 31 | PathComponents []string `json:"pathComponents"` // Path to the directory using each path component as an array item. 32 | } 33 | 34 | type FileSystem struct { 35 | target gcdmessage.ChromeTargeter 36 | } 37 | 38 | func NewFileSystem(target gcdmessage.ChromeTargeter) *FileSystem { 39 | c := &FileSystem{target: target} 40 | return c 41 | } 42 | 43 | type FileSystemGetDirectoryParams struct { 44 | // 45 | BucketFileSystemLocator *FileSystemBucketFileSystemLocator `json:"bucketFileSystemLocator"` 46 | } 47 | 48 | // GetDirectoryWithParams - 49 | // Returns - directory - Returns the directory object at the path. 50 | func (c *FileSystem) GetDirectoryWithParams(ctx context.Context, v *FileSystemGetDirectoryParams) (*FileSystemDirectory, error) { 51 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "FileSystem.getDirectory", Params: v}) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | var chromeData struct { 57 | gcdmessage.ChromeErrorResponse 58 | Result struct { 59 | Directory *FileSystemDirectory 60 | } 61 | } 62 | 63 | if resp == nil { 64 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 65 | } 66 | 67 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 68 | return nil, err 69 | } 70 | 71 | if chromeData.Error != nil { 72 | return nil, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 73 | } 74 | 75 | return chromeData.Result.Directory, nil 76 | } 77 | 78 | // GetDirectory - 79 | // bucketFileSystemLocator - 80 | // Returns - directory - Returns the directory object at the path. 81 | func (c *FileSystem) GetDirectory(ctx context.Context, bucketFileSystemLocator *FileSystemBucketFileSystemLocator) (*FileSystemDirectory, error) { 82 | var v FileSystemGetDirectoryParams 83 | v.BucketFileSystemLocator = bucketFileSystemLocator 84 | return c.GetDirectoryWithParams(ctx, &v) 85 | } 86 | -------------------------------------------------------------------------------- /v2/gcdapi/inspector.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Inspector functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Fired when remote debugging connection is about to be terminated. Contains detach reason. 13 | type InspectorDetachedEvent struct { 14 | Method string `json:"method"` 15 | Params struct { 16 | Reason string `json:"reason"` // The reason why connection has been terminated. 17 | } `json:"Params,omitempty"` 18 | } 19 | 20 | type Inspector struct { 21 | target gcdmessage.ChromeTargeter 22 | } 23 | 24 | func NewInspector(target gcdmessage.ChromeTargeter) *Inspector { 25 | c := &Inspector{target: target} 26 | return c 27 | } 28 | 29 | // Disables inspector domain notifications. 30 | func (c *Inspector) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 31 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Inspector.disable"}) 32 | } 33 | 34 | // Enables inspector domain notifications. 35 | func (c *Inspector) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 36 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Inspector.enable"}) 37 | } 38 | -------------------------------------------------------------------------------- /v2/gcdapi/io.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains IO functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | type IO struct { 13 | target gcdmessage.ChromeTargeter 14 | } 15 | 16 | func NewIO(target gcdmessage.ChromeTargeter) *IO { 17 | c := &IO{target: target} 18 | return c 19 | } 20 | 21 | type IOCloseParams struct { 22 | // Handle of the stream to close. 23 | Handle string `json:"handle"` 24 | } 25 | 26 | // CloseWithParams - Close the stream, discard any temporary backing storage. 27 | func (c *IO) CloseWithParams(ctx context.Context, v *IOCloseParams) (*gcdmessage.ChromeResponse, error) { 28 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.close", Params: v}) 29 | } 30 | 31 | // Close - Close the stream, discard any temporary backing storage. 32 | // handle - Handle of the stream to close. 33 | func (c *IO) Close(ctx context.Context, handle string) (*gcdmessage.ChromeResponse, error) { 34 | var v IOCloseParams 35 | v.Handle = handle 36 | return c.CloseWithParams(ctx, &v) 37 | } 38 | 39 | type IOReadParams struct { 40 | // Handle of the stream to read. 41 | Handle string `json:"handle"` 42 | // Seek to the specified offset before reading (if not specified, proceed with offset following the last read). Some types of streams may only support sequential reads. 43 | Offset int `json:"offset,omitempty"` 44 | // Maximum number of bytes to read (left upon the agent discretion if not specified). 45 | Size int `json:"size,omitempty"` 46 | } 47 | 48 | // ReadWithParams - Read a chunk of the stream 49 | // Returns - base64Encoded - Set if the data is base64-encoded data - Data that were read. eof - Set if the end-of-file condition occurred while reading. 50 | func (c *IO) ReadWithParams(ctx context.Context, v *IOReadParams) (bool, string, bool, error) { 51 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.read", Params: v}) 52 | if err != nil { 53 | return false, "", false, err 54 | } 55 | 56 | var chromeData struct { 57 | gcdmessage.ChromeErrorResponse 58 | Result struct { 59 | Base64Encoded bool 60 | Data string 61 | Eof bool 62 | } 63 | } 64 | 65 | if resp == nil { 66 | return false, "", false, &gcdmessage.ChromeEmptyResponseErr{} 67 | } 68 | 69 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 70 | return false, "", false, err 71 | } 72 | 73 | if chromeData.Error != nil { 74 | return false, "", false, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 75 | } 76 | 77 | return chromeData.Result.Base64Encoded, chromeData.Result.Data, chromeData.Result.Eof, nil 78 | } 79 | 80 | // Read - Read a chunk of the stream 81 | // handle - Handle of the stream to read. 82 | // offset - Seek to the specified offset before reading (if not specified, proceed with offset following the last read). Some types of streams may only support sequential reads. 83 | // size - Maximum number of bytes to read (left upon the agent discretion if not specified). 84 | // Returns - base64Encoded - Set if the data is base64-encoded data - Data that were read. eof - Set if the end-of-file condition occurred while reading. 85 | func (c *IO) Read(ctx context.Context, handle string, offset int, size int) (bool, string, bool, error) { 86 | var v IOReadParams 87 | v.Handle = handle 88 | v.Offset = offset 89 | v.Size = size 90 | return c.ReadWithParams(ctx, &v) 91 | } 92 | 93 | type IOResolveBlobParams struct { 94 | // Object id of a Blob object wrapper. 95 | ObjectId string `json:"objectId"` 96 | } 97 | 98 | // ResolveBlobWithParams - Return UUID of Blob object specified by a remote object id. 99 | // Returns - uuid - UUID of the specified Blob. 100 | func (c *IO) ResolveBlobWithParams(ctx context.Context, v *IOResolveBlobParams) (string, error) { 101 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "IO.resolveBlob", Params: v}) 102 | if err != nil { 103 | return "", err 104 | } 105 | 106 | var chromeData struct { 107 | gcdmessage.ChromeErrorResponse 108 | Result struct { 109 | Uuid string 110 | } 111 | } 112 | 113 | if resp == nil { 114 | return "", &gcdmessage.ChromeEmptyResponseErr{} 115 | } 116 | 117 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 118 | return "", err 119 | } 120 | 121 | if chromeData.Error != nil { 122 | return "", &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 123 | } 124 | 125 | return chromeData.Result.Uuid, nil 126 | } 127 | 128 | // ResolveBlob - Return UUID of Blob object specified by a remote object id. 129 | // objectId - Object id of a Blob object wrapper. 130 | // Returns - uuid - UUID of the specified Blob. 131 | func (c *IO) ResolveBlob(ctx context.Context, objectId string) (string, error) { 132 | var v IOResolveBlobParams 133 | v.ObjectId = objectId 134 | return c.ResolveBlobWithParams(ctx, &v) 135 | } 136 | -------------------------------------------------------------------------------- /v2/gcdapi/log.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Log functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Log entry. 13 | type LogLogEntry struct { 14 | Source string `json:"source"` // Log entry source. 15 | Level string `json:"level"` // Log entry severity. 16 | Text string `json:"text"` // Logged text. 17 | Category string `json:"category,omitempty"` // 18 | Timestamp float64 `json:"timestamp"` // Timestamp when this entry was added. 19 | Url string `json:"url,omitempty"` // URL of the resource if known. 20 | LineNumber int `json:"lineNumber,omitempty"` // Line number in the resource. 21 | StackTrace *RuntimeStackTrace `json:"stackTrace,omitempty"` // JavaScript stack trace. 22 | NetworkRequestId string `json:"networkRequestId,omitempty"` // Identifier of the network request associated with this entry. 23 | WorkerId string `json:"workerId,omitempty"` // Identifier of the worker associated with this entry. 24 | Args []*RuntimeRemoteObject `json:"args,omitempty"` // Call arguments. 25 | } 26 | 27 | // Violation configuration setting. 28 | type LogViolationSetting struct { 29 | Name string `json:"name"` // Violation type. 30 | Threshold float64 `json:"threshold"` // Time threshold to trigger upon. 31 | } 32 | 33 | // Issued when new message was logged. 34 | type LogEntryAddedEvent struct { 35 | Method string `json:"method"` 36 | Params struct { 37 | Entry *LogLogEntry `json:"entry"` // The entry. 38 | } `json:"Params,omitempty"` 39 | } 40 | 41 | type Log struct { 42 | target gcdmessage.ChromeTargeter 43 | } 44 | 45 | func NewLog(target gcdmessage.ChromeTargeter) *Log { 46 | c := &Log{target: target} 47 | return c 48 | } 49 | 50 | // Clears the log. 51 | func (c *Log) Clear(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 52 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.clear"}) 53 | } 54 | 55 | // Disables log domain, prevents further log entries from being reported to the client. 56 | func (c *Log) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 57 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.disable"}) 58 | } 59 | 60 | // Enables log domain, sends the entries collected so far to the client by means of the `entryAdded` notification. 61 | func (c *Log) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 62 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.enable"}) 63 | } 64 | 65 | type LogStartViolationsReportParams struct { 66 | // Configuration for violations. 67 | Config []*LogViolationSetting `json:"config"` 68 | } 69 | 70 | // StartViolationsReportWithParams - start violation reporting. 71 | func (c *Log) StartViolationsReportWithParams(ctx context.Context, v *LogStartViolationsReportParams) (*gcdmessage.ChromeResponse, error) { 72 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.startViolationsReport", Params: v}) 73 | } 74 | 75 | // StartViolationsReport - start violation reporting. 76 | // config - Configuration for violations. 77 | func (c *Log) StartViolationsReport(ctx context.Context, config []*LogViolationSetting) (*gcdmessage.ChromeResponse, error) { 78 | var v LogStartViolationsReportParams 79 | v.Config = config 80 | return c.StartViolationsReportWithParams(ctx, &v) 81 | } 82 | 83 | // Stop violation reporting. 84 | func (c *Log) StopViolationsReport(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 85 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Log.stopViolationsReport"}) 86 | } 87 | -------------------------------------------------------------------------------- /v2/gcdapi/media.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Media functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Have one type per entry in MediaLogRecord::Type Corresponds to kMessage 13 | type MediaPlayerMessage struct { 14 | Level string `json:"level"` // Keep in sync with MediaLogMessageLevel We are currently keeping the message level 'error' separate from the PlayerError type because right now they represent different things, this one being a DVLOG(ERROR) style log message that gets printed based on what log level is selected in the UI, and the other is a representation of a media::PipelineStatus object. Soon however we're going to be moving away from using PipelineStatus for errors and introducing a new error type which should hopefully let us integrate the error log level into the PlayerError type. 15 | Message string `json:"message"` // 16 | } 17 | 18 | // Corresponds to kMediaPropertyChange 19 | type MediaPlayerProperty struct { 20 | Name string `json:"name"` // 21 | Value string `json:"value"` // 22 | } 23 | 24 | // Corresponds to kMediaEventTriggered 25 | type MediaPlayerEvent struct { 26 | Timestamp float64 `json:"timestamp"` // 27 | Value string `json:"value"` // 28 | } 29 | 30 | // Represents logged source line numbers reported in an error. NOTE: file and line are from chromium c++ implementation code, not js. 31 | type MediaPlayerErrorSourceLocation struct { 32 | File string `json:"file"` // 33 | Line int `json:"line"` // 34 | } 35 | 36 | // Corresponds to kMediaError 37 | type MediaPlayerError struct { 38 | ErrorType string `json:"errorType"` // 39 | Code int `json:"code"` // Code is the numeric enum entry for a specific set of error codes, such as PipelineStatusCodes in media/base/pipeline_status.h 40 | Stack []*MediaPlayerErrorSourceLocation `json:"stack"` // A trace of where this error was caused / where it passed through. 41 | Cause []*MediaPlayerError `json:"cause"` // Errors potentially have a root cause error, ie, a DecoderError might be caused by an WindowsError 42 | Data map[string]interface{} `json:"data"` // Extra data attached to an error, such as an HRESULT, Video Codec, etc. 43 | } 44 | 45 | // This can be called multiple times, and can be used to set / override / remove player properties. A null propValue indicates removal. 46 | type MediaPlayerPropertiesChangedEvent struct { 47 | Method string `json:"method"` 48 | Params struct { 49 | PlayerId string `json:"playerId"` // 50 | Properties []*MediaPlayerProperty `json:"properties"` // 51 | } `json:"Params,omitempty"` 52 | } 53 | 54 | // Send events as a list, allowing them to be batched on the browser for less congestion. If batched, events must ALWAYS be in chronological order. 55 | type MediaPlayerEventsAddedEvent struct { 56 | Method string `json:"method"` 57 | Params struct { 58 | PlayerId string `json:"playerId"` // 59 | Events []*MediaPlayerEvent `json:"events"` // 60 | } `json:"Params,omitempty"` 61 | } 62 | 63 | // Send a list of any messages that need to be delivered. 64 | type MediaPlayerMessagesLoggedEvent struct { 65 | Method string `json:"method"` 66 | Params struct { 67 | PlayerId string `json:"playerId"` // 68 | Messages []*MediaPlayerMessage `json:"messages"` // 69 | } `json:"Params,omitempty"` 70 | } 71 | 72 | // Send a list of any errors that need to be delivered. 73 | type MediaPlayerErrorsRaisedEvent struct { 74 | Method string `json:"method"` 75 | Params struct { 76 | PlayerId string `json:"playerId"` // 77 | Errors []*MediaPlayerError `json:"errors"` // 78 | } `json:"Params,omitempty"` 79 | } 80 | 81 | // Called whenever a player is created, or when a new agent joins and receives a list of active players. If an agent is restored, it will receive the full list of player ids and all events again. 82 | type MediaPlayersCreatedEvent struct { 83 | Method string `json:"method"` 84 | Params struct { 85 | Players []string `json:"players"` // 86 | } `json:"Params,omitempty"` 87 | } 88 | 89 | type Media struct { 90 | target gcdmessage.ChromeTargeter 91 | } 92 | 93 | func NewMedia(target gcdmessage.ChromeTargeter) *Media { 94 | c := &Media{target: target} 95 | return c 96 | } 97 | 98 | // Enables the Media domain 99 | func (c *Media) Enable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 100 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Media.enable"}) 101 | } 102 | 103 | // Disables the Media domain. 104 | func (c *Media) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 105 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Media.disable"}) 106 | } 107 | -------------------------------------------------------------------------------- /v2/gcdapi/performance.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Performance functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Run-time execution metric. 13 | type PerformanceMetric struct { 14 | Name string `json:"name"` // Metric name. 15 | Value float64 `json:"value"` // Metric value. 16 | } 17 | 18 | // Current values of the metrics. 19 | type PerformanceMetricsEvent struct { 20 | Method string `json:"method"` 21 | Params struct { 22 | Metrics []*PerformanceMetric `json:"metrics"` // Current values of the metrics. 23 | Title string `json:"title"` // Timestamp title. 24 | } `json:"Params,omitempty"` 25 | } 26 | 27 | type Performance struct { 28 | target gcdmessage.ChromeTargeter 29 | } 30 | 31 | func NewPerformance(target gcdmessage.ChromeTargeter) *Performance { 32 | c := &Performance{target: target} 33 | return c 34 | } 35 | 36 | // Disable collecting and reporting metrics. 37 | func (c *Performance) Disable(ctx context.Context) (*gcdmessage.ChromeResponse, error) { 38 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.disable"}) 39 | } 40 | 41 | type PerformanceEnableParams struct { 42 | // Time domain to use for collecting and reporting duration metrics. 43 | TimeDomain string `json:"timeDomain,omitempty"` 44 | } 45 | 46 | // EnableWithParams - Enable collecting and reporting metrics. 47 | func (c *Performance) EnableWithParams(ctx context.Context, v *PerformanceEnableParams) (*gcdmessage.ChromeResponse, error) { 48 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.enable", Params: v}) 49 | } 50 | 51 | // Enable - Enable collecting and reporting metrics. 52 | // timeDomain - Time domain to use for collecting and reporting duration metrics. 53 | func (c *Performance) Enable(ctx context.Context, timeDomain string) (*gcdmessage.ChromeResponse, error) { 54 | var v PerformanceEnableParams 55 | v.TimeDomain = timeDomain 56 | return c.EnableWithParams(ctx, &v) 57 | } 58 | 59 | type PerformanceSetTimeDomainParams struct { 60 | // Time domain 61 | TimeDomain string `json:"timeDomain"` 62 | } 63 | 64 | // SetTimeDomainWithParams - Sets time domain to use for collecting and reporting duration metrics. Note that this must be called before enabling metrics collection. Calling this method while metrics collection is enabled returns an error. 65 | func (c *Performance) SetTimeDomainWithParams(ctx context.Context, v *PerformanceSetTimeDomainParams) (*gcdmessage.ChromeResponse, error) { 66 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.setTimeDomain", Params: v}) 67 | } 68 | 69 | // SetTimeDomain - Sets time domain to use for collecting and reporting duration metrics. Note that this must be called before enabling metrics collection. Calling this method while metrics collection is enabled returns an error. 70 | // timeDomain - Time domain 71 | func (c *Performance) SetTimeDomain(ctx context.Context, timeDomain string) (*gcdmessage.ChromeResponse, error) { 72 | var v PerformanceSetTimeDomainParams 73 | v.TimeDomain = timeDomain 74 | return c.SetTimeDomainWithParams(ctx, &v) 75 | } 76 | 77 | // GetMetrics - Retrieve current values of run-time metrics. 78 | // Returns - metrics - Current values for run-time metrics. 79 | func (c *Performance) GetMetrics(ctx context.Context) ([]*PerformanceMetric, error) { 80 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Performance.getMetrics"}) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | var chromeData struct { 86 | gcdmessage.ChromeErrorResponse 87 | Result struct { 88 | Metrics []*PerformanceMetric 89 | } 90 | } 91 | 92 | if resp == nil { 93 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 94 | } 95 | 96 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 97 | return nil, err 98 | } 99 | 100 | if chromeData.Error != nil { 101 | return nil, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 102 | } 103 | 104 | return chromeData.Result.Metrics, nil 105 | } 106 | -------------------------------------------------------------------------------- /v2/gcdapi/performancetimeline.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains PerformanceTimeline functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // See https://github.com/WICG/LargestContentfulPaint and largest_contentful_paint.idl 13 | type PerformanceTimelineLargestContentfulPaint struct { 14 | RenderTime float64 `json:"renderTime"` // 15 | LoadTime float64 `json:"loadTime"` // 16 | Size float64 `json:"size"` // The number of pixels being painted. 17 | ElementId string `json:"elementId,omitempty"` // The id attribute of the element, if available. 18 | Url string `json:"url,omitempty"` // The URL of the image (may be trimmed). 19 | NodeId int `json:"nodeId,omitempty"` // 20 | } 21 | 22 | // No Description. 23 | type PerformanceTimelineLayoutShiftAttribution struct { 24 | PreviousRect *DOMRect `json:"previousRect"` // 25 | CurrentRect *DOMRect `json:"currentRect"` // 26 | NodeId int `json:"nodeId,omitempty"` // 27 | } 28 | 29 | // See https://wicg.github.io/layout-instability/#sec-layout-shift and layout_shift.idl 30 | type PerformanceTimelineLayoutShift struct { 31 | Value float64 `json:"value"` // Score increment produced by this event. 32 | HadRecentInput bool `json:"hadRecentInput"` // 33 | LastInputTime float64 `json:"lastInputTime"` // 34 | Sources []*PerformanceTimelineLayoutShiftAttribution `json:"sources"` // 35 | } 36 | 37 | // No Description. 38 | type PerformanceTimelineTimelineEvent struct { 39 | FrameId string `json:"frameId"` // Identifies the frame that this event is related to. Empty for non-frame targets. 40 | Type string `json:"type"` // The event type, as specified in https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype This determines which of the optional "details" fields is present. 41 | Name string `json:"name"` // Name may be empty depending on the type. 42 | Time float64 `json:"time"` // Time in seconds since Epoch, monotonically increasing within document lifetime. 43 | Duration float64 `json:"duration,omitempty"` // Event duration, if applicable. 44 | LcpDetails *PerformanceTimelineLargestContentfulPaint `json:"lcpDetails,omitempty"` // 45 | LayoutShiftDetails *PerformanceTimelineLayoutShift `json:"layoutShiftDetails,omitempty"` // 46 | } 47 | 48 | // Sent when a performance timeline event is added. See reportPerformanceTimeline method. 49 | type PerformanceTimelineTimelineEventAddedEvent struct { 50 | Method string `json:"method"` 51 | Params struct { 52 | Event *PerformanceTimelineTimelineEvent `json:"event"` // 53 | } `json:"Params,omitempty"` 54 | } 55 | 56 | type PerformanceTimeline struct { 57 | target gcdmessage.ChromeTargeter 58 | } 59 | 60 | func NewPerformanceTimeline(target gcdmessage.ChromeTargeter) *PerformanceTimeline { 61 | c := &PerformanceTimeline{target: target} 62 | return c 63 | } 64 | 65 | type PerformanceTimelineEnableParams struct { 66 | // The types of event to report, as specified in https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype The specified filter overrides any previous filters, passing empty filter disables recording. Note that not all types exposed to the web platform are currently supported. 67 | EventTypes []string `json:"eventTypes"` 68 | } 69 | 70 | // EnableWithParams - Previously buffered events would be reported before method returns. See also: timelineEventAdded 71 | func (c *PerformanceTimeline) EnableWithParams(ctx context.Context, v *PerformanceTimelineEnableParams) (*gcdmessage.ChromeResponse, error) { 72 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "PerformanceTimeline.enable", Params: v}) 73 | } 74 | 75 | // Enable - Previously buffered events would be reported before method returns. See also: timelineEventAdded 76 | // eventTypes - The types of event to report, as specified in https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype The specified filter overrides any previous filters, passing empty filter disables recording. Note that not all types exposed to the web platform are currently supported. 77 | func (c *PerformanceTimeline) Enable(ctx context.Context, eventTypes []string) (*gcdmessage.ChromeResponse, error) { 78 | var v PerformanceTimelineEnableParams 79 | v.EventTypes = eventTypes 80 | return c.EnableWithParams(ctx, &v) 81 | } 82 | -------------------------------------------------------------------------------- /v2/gcdapi/schema.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Schema functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Description of the protocol domain. 13 | type SchemaDomain struct { 14 | Name string `json:"name"` // Domain name. 15 | Version string `json:"version"` // Domain version. 16 | } 17 | 18 | type Schema struct { 19 | target gcdmessage.ChromeTargeter 20 | } 21 | 22 | func NewSchema(target gcdmessage.ChromeTargeter) *Schema { 23 | c := &Schema{target: target} 24 | return c 25 | } 26 | 27 | // GetDomains - Returns supported domains. 28 | // Returns - domains - List of supported domains. 29 | func (c *Schema) GetDomains(ctx context.Context) ([]*SchemaDomain, error) { 30 | resp, err := c.target.SendCustomReturn(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Schema.getDomains"}) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | var chromeData struct { 36 | gcdmessage.ChromeErrorResponse 37 | Result struct { 38 | Domains []*SchemaDomain 39 | } 40 | } 41 | 42 | if resp == nil { 43 | return nil, &gcdmessage.ChromeEmptyResponseErr{} 44 | } 45 | 46 | if err := jsonUnmarshal(resp.Data, &chromeData); err != nil { 47 | return nil, err 48 | } 49 | 50 | if chromeData.Error != nil { 51 | return nil, &gcdmessage.ChromeRequestErr{Resp: &chromeData.ChromeErrorResponse} 52 | } 53 | 54 | return chromeData.Result.Domains, nil 55 | } 56 | -------------------------------------------------------------------------------- /v2/gcdapi/tethering.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED Chrome Remote Debugger Protocol API Client 2 | // This file contains Tethering functionality. 3 | // API Version: 1.3 4 | 5 | package gcdapi 6 | 7 | import ( 8 | "context" 9 | "github.com/wirepair/gcd/v2/gcdmessage" 10 | ) 11 | 12 | // Informs that port was successfully bound and got a specified connection id. 13 | type TetheringAcceptedEvent struct { 14 | Method string `json:"method"` 15 | Params struct { 16 | Port int `json:"port"` // Port number that was successfully bound. 17 | ConnectionId string `json:"connectionId"` // Connection id to be used. 18 | } `json:"Params,omitempty"` 19 | } 20 | 21 | type Tethering struct { 22 | target gcdmessage.ChromeTargeter 23 | } 24 | 25 | func NewTethering(target gcdmessage.ChromeTargeter) *Tethering { 26 | c := &Tethering{target: target} 27 | return c 28 | } 29 | 30 | type TetheringBindParams struct { 31 | // Port number to bind. 32 | Port int `json:"port"` 33 | } 34 | 35 | // BindWithParams - Request browser port binding. 36 | func (c *Tethering) BindWithParams(ctx context.Context, v *TetheringBindParams) (*gcdmessage.ChromeResponse, error) { 37 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Tethering.bind", Params: v}) 38 | } 39 | 40 | // Bind - Request browser port binding. 41 | // port - Port number to bind. 42 | func (c *Tethering) Bind(ctx context.Context, port int) (*gcdmessage.ChromeResponse, error) { 43 | var v TetheringBindParams 44 | v.Port = port 45 | return c.BindWithParams(ctx, &v) 46 | } 47 | 48 | type TetheringUnbindParams struct { 49 | // Port number to unbind. 50 | Port int `json:"port"` 51 | } 52 | 53 | // UnbindWithParams - Request browser port unbinding. 54 | func (c *Tethering) UnbindWithParams(ctx context.Context, v *TetheringUnbindParams) (*gcdmessage.ChromeResponse, error) { 55 | return c.target.SendDefaultRequest(ctx, &gcdmessage.ParamRequest{Id: c.target.GetId(), Method: "Tethering.unbind", Params: v}) 56 | } 57 | 58 | // Unbind - Request browser port unbinding. 59 | // port - Port number to unbind. 60 | func (c *Tethering) Unbind(ctx context.Context, port int) (*gcdmessage.ChromeResponse, error) { 61 | var v TetheringUnbindParams 62 | v.Port = port 63 | return c.UnbindWithParams(ctx, &v) 64 | } 65 | -------------------------------------------------------------------------------- /v2/gcdapi/version.go: -------------------------------------------------------------------------------- 1 | package gcdapi 2 | 3 | import "github.com/goccy/go-json" 4 | 5 | // define json here for all Domains 6 | // useful so that each generated file doesn't need to know whether to import the JSON library 7 | var jsonUnmarshal = json.Unmarshal 8 | 9 | // Chrome Channel information 10 | const CHROME_CHANNEL = "unknown" 11 | // Chrome Version information 12 | const CHROME_VERSION = "unknown" 13 | // Protocol Major version 14 | const DEVTOOLS_MAJOR_VERSION = "1" 15 | // Protocol Minor version 16 | const DEVTOOLS_MINOR_VERSION = "3" -------------------------------------------------------------------------------- /v2/gcdapigen/README.md: -------------------------------------------------------------------------------- 1 | # Google Chrome Debugger (GCD) API Generator 2 | You should hopefully not have to use this, but this package is for generating commands, types and events for the Google Chrome Debugger Service for remotely debugging a Chrome Process. 3 | 4 | Basically it reads in the protocol.json file, does some gnarly processing and spits out the code using templates. All references are looked up so we don't have to do nasty type conversions just to get to a base underlying type. This was pretty much a full rewrite, moved all API stuff to it's own package so it doesn't pollute the gcd package. 5 | 6 | Updated September 2015 with latest protocol.json. 7 | 8 | If you plan on downloading later versions of protocol.json, keep in mind there are some things you'll need to fix by hand. In particular one field which *should* be a bool is actually a string of "true". Go's JSON error handling is absolutely horrible in that it won't tell you the exact line the unmarshalling fails on so I had to fix it by hand. 9 | 10 | ## Developer Notes 11 | The protocol.json file while appears simple at first is pretty complex, with lots of crazy edge cases and variables, all around how they use '$ref' fields. You'll notice a *lot* of conditionals when looking up references, i just saw no other way to accomplish generating types without having hundereds of dumb type SomeObject string or type SomeOtherObject []int everywhere. To make using the API easier, i resolved all of these references (at least I think I did) and tried to limit the amount of work necessary for unmarshalling the various data types requires. 12 | 13 | If you need to fix references, the two main areas are utils.go:PopulateReferences and domain.go:resolveReferences. We first loop over the entire set of domains and make a map of all reference types, storing various critical metadata about the references (is it array, is it a ref to a underlying type, etc). As types are generated, it looks at this map and replaces any definitions with the proper ones. 14 | 15 | ## Usage 16 | Download the latest [protocol.json](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/devtools/protocol.json&q=protocol.json&sq=package:chromium&type=cs) and make sure there aren't any string encoded bools (usually hidden or optional fields), fix if necessary, then run: 17 | ``` 18 | go build 19 | ./gcdapigen protocol.json 20 | cp -R output/. ../gcdapi 21 | cd !$ 22 | go build && go install 23 | ``` 24 | 25 | ## Licensing 26 | The MIT License (MIT) 27 | 28 | Copyright (c) 2015 isaac dawson 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy 31 | of this software and associated documentation files (the "Software"), to deal 32 | in the Software without restriction, including without limitation the rights 33 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | copies of the Software, and to permit persons to whom the Software is 35 | furnished to do so, subject to the following conditions: 36 | 37 | The above copyright notice and this permission notice shall be included in 38 | all copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | THE SOFTWARE. 47 | -------------------------------------------------------------------------------- /v2/gcdapigen/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go build 4 | ./gcdapigen protocol.json 5 | cp -R output/. ../gcdapi 6 | cd ../ && go build && go install 7 | -------------------------------------------------------------------------------- /v2/gcdapigen/command.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Command struct { 28 | Name string 29 | Description string 30 | Parameters []*TypeProperties 31 | Returns []*Return 32 | HasParams bool 33 | HasReturn bool 34 | NoParamReturnCalls bool 35 | ParamCalls bool 36 | ReturnCalls bool 37 | ParamReturnCalls bool 38 | } 39 | 40 | func NewCommand(protoCommand *ProtoCommand) *Command { 41 | c := &Command{} 42 | c.Name = protoCommand.Name 43 | c.Description = protoCommand.Description 44 | if protoCommand.Parameters != nil && len(protoCommand.Parameters) > 0 { 45 | c.HasParams = true 46 | } 47 | 48 | if protoCommand.Returns != nil && len(protoCommand.Returns) > 0 { 49 | c.HasReturn = true 50 | } 51 | // Determine type of call for template output 52 | if c.HasParams == false && c.HasReturn == false { 53 | c.NoParamReturnCalls = true 54 | } 55 | 56 | if c.HasParams == true && c.HasReturn == false { 57 | c.ParamCalls = true 58 | } 59 | 60 | if c.HasParams == false && c.HasReturn == true { 61 | c.ReturnCalls = true 62 | } 63 | 64 | if c.HasParams == true && c.HasReturn == true { 65 | c.ParamReturnCalls = true 66 | } 67 | 68 | c.Returns = make([]*Return, 0) 69 | c.Parameters = make([]*TypeProperties, 0) 70 | return c 71 | } 72 | -------------------------------------------------------------------------------- /v2/gcdapigen/downloader.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "bytes" 29 | "encoding/json" 30 | "io/ioutil" 31 | "log" 32 | "net/http" 33 | "os" 34 | ) 35 | 36 | const ( 37 | browserProtocolFile = "https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/refs/heads/master/json/browser_protocol.json" 38 | jsProtocolFile = "https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/refs/heads/master/json/js_protocol.json" 39 | ) 40 | 41 | func download(browserFile, jsFile string) { 42 | browserData := fixBrokenBool(getRemoteFile(browserFile)) 43 | jsData := fixBrokenBool(getRemoteFile(jsFile)) 44 | 45 | api := &ProtoDebuggerApi{} 46 | copyApi := &ProtoDebuggerApi{} 47 | 48 | if err := json.Unmarshal(browserData, api); err != nil { 49 | log.Fatalf("error unmarshalling browser data: %s\n", err) 50 | } 51 | log.Printf("len: %d\n", len(api.Domains)) 52 | 53 | // add js data to our api 54 | if err := json.Unmarshal(jsData, copyApi); err != nil { 55 | log.Fatalf("error unmarshalling js data: %s\n", err) 56 | } 57 | 58 | // append Domain entries 59 | api.Domains = append(api.Domains, copyApi.Domains...) 60 | log.Printf("len: %d\n", len(api.Domains)) 61 | merged, err := json.Marshal(api) 62 | if err != nil { 63 | log.Fatalf("error marshaling merged protocol data: %s\n", err) 64 | } 65 | writeFile(merged) 66 | } 67 | 68 | func getRemoteFile(fileName string) []byte { 69 | var data []byte 70 | 71 | log.Printf("requesting: %s\n", fileName) 72 | resp, err := http.Get(fileName) 73 | if err != nil { 74 | log.Fatalf("error requesting %s\n", fileName) 75 | } 76 | defer resp.Body.Close() 77 | 78 | if data, err = ioutil.ReadAll(resp.Body); err != nil { 79 | log.Fatalf("error reading data from response: %s\n", err) 80 | } 81 | 82 | return data 83 | } 84 | 85 | func fixBrokenBool(data []byte) []byte { 86 | return bytes.Replace(data, []byte("\"true\""), []byte("true"), -1) 87 | } 88 | 89 | func writeFile(protocolData []byte) { 90 | var f *os.File 91 | f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 92 | if err != nil { 93 | log.Fatalf("error opening file %s for writing: %s\n", file, err) 94 | } 95 | protocolData = bytes.Replace(protocolData, []byte("\\n"), []byte(" "), -1) // remove newlines 96 | f.Write(protocolData) 97 | f.Sync() 98 | f.Close() 99 | } 100 | -------------------------------------------------------------------------------- /v2/gcdapigen/event.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Event struct { 28 | protoEvent *ProtoEvent 29 | Name string 30 | Description string 31 | Parameters []*TypeProperties 32 | HasParams bool 33 | } 34 | 35 | func NewEvent(protoEvent *ProtoEvent) *Event { 36 | e := &Event{} 37 | e.Name = protoEvent.Name 38 | e.Description = protoEvent.Description 39 | if protoEvent.Parameters != nil && len(protoEvent.Parameters) > 0 { 40 | e.HasParams = true 41 | } 42 | e.Parameters = make([]*TypeProperties, 0) 43 | return e 44 | } 45 | -------------------------------------------------------------------------------- /v2/gcdapigen/return.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | type Return struct { 28 | protoReturn *ProtoCommandReturns 29 | Name string // property name 30 | Description string // property description 31 | UnderlyingType string 32 | GoType string 33 | Optional bool // is this property optional? 34 | EnumVals string // possible enum values as a string 35 | IsRef bool // is a reference to another type 36 | IsTypeArray bool // for templates to spit out [] 37 | IsPointer bool 38 | Ref string 39 | } 40 | 41 | func NewReturn(protoReturn *ProtoCommandReturns) *Return { 42 | r := &Return{} 43 | r.protoReturn = protoReturn 44 | r.Name = protoReturn.Name 45 | r.Description = protoReturn.Description 46 | r.UnderlyingType = protoReturn.Type 47 | r.Ref = protoReturn.Ref 48 | 49 | if protoReturn.Ref != "" { 50 | r.IsRef = true 51 | } 52 | 53 | // if array, check underlying array type to see if it's a reference 54 | if r.IsArray() { 55 | r.IsTypeArray = true 56 | if arrayRef := r.ArrayRef(); arrayRef != "" { 57 | r.Ref = arrayRef 58 | r.IsRef = true 59 | } 60 | } 61 | 62 | return r 63 | } 64 | 65 | func (r *Return) ArrayRef() string { 66 | if r.protoReturn.Items.Ref != "" { 67 | return r.protoReturn.Items.Ref 68 | } 69 | return "" 70 | } 71 | 72 | // PropSetter interface methods 73 | func (r *Return) GetGoType() string { 74 | return r.GoType 75 | } 76 | 77 | func (r *Return) SetIsTypeArray(isTypeArray bool) { 78 | r.IsTypeArray = true 79 | } 80 | 81 | func (r *Return) GetIsTypeArray() bool { 82 | return r.IsTypeArray 83 | } 84 | 85 | func (r *Return) GetEnumVals() string { 86 | return r.EnumVals 87 | } 88 | 89 | func (r *Return) GetRef() string { 90 | return r.Ref 91 | } 92 | 93 | func (r *Return) SetIsRef(isRef bool) { 94 | r.IsRef = isRef 95 | } 96 | 97 | func (r *Return) GetIsRef() bool { 98 | return r.IsRef 99 | } 100 | 101 | func (r *Return) SetGoType(goType string) { 102 | r.GoType = goType 103 | } 104 | 105 | func (r *Return) GetDescription() string { 106 | return r.Description 107 | } 108 | func (r *Return) SetDescription(description string) { 109 | r.Description = description 110 | } 111 | 112 | func (r *Return) SetPointerType(isPointer bool) { 113 | r.IsPointer = isPointer 114 | } 115 | 116 | // SharedProperties interface methods 117 | func (r *Return) IsNonPropertiesObject() bool { 118 | return (r.UnderlyingType == "object") // return with object type never has properties // && len(r.protoReturn.Properties) == 0) 119 | } 120 | 121 | func (r *Return) GetUnderlyingType() string { 122 | return r.UnderlyingType 123 | } 124 | 125 | func (r *Return) IsArray() bool { 126 | return r.UnderlyingType == "array" 127 | } 128 | 129 | func (r *Return) GetArrayType() string { 130 | if r.protoReturn.Items.Type != "" { 131 | return r.protoReturn.Items.Type 132 | } 133 | 134 | if r.protoReturn.Items.Ref != "" { 135 | return r.protoReturn.Items.Ref 136 | } 137 | return "object" 138 | } 139 | -------------------------------------------------------------------------------- /v2/gcdapigen/template_funcs.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "strings" 29 | ) 30 | 31 | func modifyReserved(input string) string { 32 | switch input { 33 | case "type": 34 | return "theType" 35 | case "range": 36 | return "theRange" 37 | case "interface": 38 | return "theInterface" 39 | case "for": 40 | return "theFor" 41 | } 42 | return input 43 | } 44 | 45 | func nullType(input string) string { 46 | if strings.Contains(input, "[]") { 47 | return "nil" 48 | } 49 | //fmt.Printf("INPUT: %s\n", input) 50 | switch input { 51 | case "int": 52 | return "0" 53 | case "float64": 54 | return "0" 55 | case "string": 56 | return "\"\"" 57 | case "bool": 58 | return "false" 59 | case "interface{}": 60 | return "nil" 61 | } 62 | return "nil" 63 | } 64 | -------------------------------------------------------------------------------- /v2/gcdapigen/type.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | import ( 28 | "strings" 29 | ) 30 | 31 | type Type struct { 32 | protoType *ProtoType 33 | Name string // the name of the API call, Event or Type 34 | Description string // the description/comments for the struct 35 | GoType string // the type this would be in Go 36 | UnderlyingType string // the type defined in protocol.json 37 | EnumVals string // if it's an enum string list out the possible values as a comment 38 | IsSubType bool // is this a sub type? (Should be prefixed with Sub in template) 39 | Properties []*TypeProperties 40 | } 41 | 42 | func NewType(protoType *ProtoType) *Type { 43 | t := &Type{} 44 | t.protoType = protoType 45 | t.Name = protoType.Id 46 | t.Description = protoType.Description 47 | if t.Description == "" { 48 | t.Description = "No Description." 49 | } 50 | t.UnderlyingType = protoType.Type 51 | t.Properties = make([]*TypeProperties, 0) 52 | return t 53 | } 54 | 55 | func NewSubType(parentProps *TypeProperties, protoProps *ProtoProperty) *Type { 56 | st := &Type{} 57 | st.IsSubType = true 58 | // only convert to type if it's an array 59 | if protoProps.IsArray() { 60 | st.protoType = typeFromProperties(protoProps) 61 | } 62 | 63 | st.Name = "Sub" + strings.Title(parentProps.Name) 64 | st.Properties = make([]*TypeProperties, 0) 65 | st.UnderlyingType = parentProps.UnderlyingType 66 | return st 67 | } 68 | 69 | func (t *Type) IsNonPropertiesObject() bool { 70 | return (t.UnderlyingType == "object" && len(t.protoType.Properties) == 0) 71 | } 72 | 73 | func (t *Type) GetUnderlyingType() string { 74 | return t.UnderlyingType 75 | } 76 | 77 | func (t *Type) IsArray() bool { 78 | return t.UnderlyingType == "array" 79 | } 80 | 81 | func (t *Type) GetArrayType() string { 82 | if t.protoType.Items.Type != "" { 83 | return t.protoType.Items.Type 84 | } 85 | 86 | if t.protoType.Items.Ref != "" { 87 | return t.protoType.Items.Ref 88 | } 89 | return "object" 90 | } 91 | -------------------------------------------------------------------------------- /v2/gcdapigen/type_properties.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | package main 26 | 27 | // Used for Types and Parameters to function calls 28 | type TypeProperties struct { 29 | protoProperty *ProtoProperty 30 | Name string // property name 31 | Description string // property description 32 | UnderlyingType string 33 | GoType string 34 | Optional bool // is this property optional? 35 | EnumVals string // possible enum values as a string 36 | Ref string 37 | IsRef bool // is a reference to another type 38 | IsPointer bool // should we output as pointer (for API types, not basic types) 39 | IsTypeArray bool // for templates to spit out [] 40 | } 41 | 42 | func NewTypeProperties(props *ProtoProperty) *TypeProperties { 43 | tp := &TypeProperties{} 44 | tp.protoProperty = props 45 | tp.Name = props.Name 46 | tp.Description = props.Description 47 | tp.Optional = props.Optional 48 | tp.Ref = props.Ref 49 | tp.UnderlyingType = props.Type 50 | if tp.IsArray() { 51 | tp.IsTypeArray = true 52 | if arrayRef := tp.ArrayRef(); arrayRef != "" { 53 | tp.Ref = arrayRef 54 | tp.IsRef = true 55 | } 56 | } 57 | return tp 58 | } 59 | 60 | // PropSetter interface methods 61 | func (p *TypeProperties) GetGoType() string { 62 | return p.GoType 63 | } 64 | 65 | func (p *TypeProperties) SetGoType(goType string) { 66 | p.GoType = goType 67 | } 68 | 69 | func (p *TypeProperties) GetIsRef() bool { 70 | return p.IsRef 71 | } 72 | 73 | func (p *TypeProperties) SetIsRef(isRef bool) { 74 | p.IsRef = isRef 75 | } 76 | 77 | func (p *TypeProperties) SetIsTypeArray(isTypeArray bool) { 78 | p.IsTypeArray = true 79 | } 80 | 81 | func (p *TypeProperties) GetIsTypeArray() bool { 82 | return p.IsTypeArray 83 | } 84 | 85 | func (p *TypeProperties) GetRef() string { 86 | return p.Ref 87 | } 88 | 89 | func (p *TypeProperties) GetEnumVals() string { 90 | return p.EnumVals 91 | } 92 | 93 | func (p *TypeProperties) GetDescription() string { 94 | return p.Description 95 | } 96 | func (p *TypeProperties) SetDescription(description string) { 97 | p.Description = description 98 | } 99 | 100 | func (p *TypeProperties) SetPointerType(isPointer bool) { 101 | p.IsPointer = isPointer 102 | } 103 | 104 | // SharedProperties interface methods 105 | func (p *TypeProperties) IsNonPropertiesObject() bool { 106 | return (p.UnderlyingType == "object" && len(p.protoProperty.Properties) == 0) 107 | } 108 | 109 | func (p *TypeProperties) GetUnderlyingType() string { 110 | return p.UnderlyingType 111 | } 112 | 113 | func (p *TypeProperties) IsArray() bool { 114 | return p.UnderlyingType == "array" 115 | } 116 | 117 | func (p *TypeProperties) GetArrayType() string { 118 | if p.protoProperty.Items.Type != "" { 119 | return p.protoProperty.Items.Type 120 | } 121 | 122 | if p.protoProperty.Items.Ref != "" { 123 | return p.protoProperty.Items.Ref 124 | } 125 | return "object" 126 | } 127 | 128 | func (p *TypeProperties) ArrayRef() string { 129 | if p.protoProperty.Items.Ref != "" { 130 | return p.protoProperty.Items.Ref 131 | } 132 | return "" 133 | } 134 | -------------------------------------------------------------------------------- /v2/gcdmessage/gcdmessage.go: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 isaac dawson 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | // This package contains messaging types and functions between the API and our gcd library. 26 | 27 | package gcdmessage 28 | 29 | import ( 30 | "context" 31 | "strconv" 32 | "time" 33 | ) 34 | 35 | type ChromeTargeter interface { 36 | GetId() int64 37 | GetApiTimeout() time.Duration 38 | GetSendCh() chan *Message 39 | GetDoneCh() chan struct{} // if tab is closed we don't want dangling goroutines. 40 | SendCustomReturn(ctx context.Context, paramRequest *ParamRequest) (*Message, error) 41 | SendDefaultRequest(ctx context.Context, paramRequest *ParamRequest) (*ChromeResponse, error) 42 | } 43 | 44 | // An internal message object used for components and ChromeTarget to communicate back and forth 45 | type Message struct { 46 | ReplyCh chan *Message // json response channel 47 | Id int64 // id to map response channels to send chans 48 | Data []byte // the data for the websocket to send/recv 49 | Method string // event name type. 50 | Target ChromeTargeter // reference to the ChromeTarget for events 51 | } 52 | 53 | // default response object, contains the id and a result if applicable. 54 | type ChromeResponse struct { 55 | Id int64 `json:"id"` 56 | Result interface{} `json:"result"` 57 | } 58 | 59 | // default no-arg request 60 | type ChromeRequest struct { 61 | Id int64 `json:"id"` 62 | Method string `json:"method"` 63 | Params interface{} `json:"params,omitempty"` 64 | } 65 | 66 | // default chrome error response to an invalid request. 67 | type ChromeErrorResponse struct { 68 | Id int64 `json:"id"` // the request Id that this is a response of 69 | Error *ChromeError `json:"error"` // the error object 70 | } 71 | 72 | // An error object returned from a request 73 | type ChromeError struct { 74 | Code int64 `json:"code"` // the error code 75 | Message string `json:"message"` // the error message 76 | } 77 | 78 | // A gcd type for reporting chrome request errors 79 | type ChromeRequestErr struct { 80 | Resp *ChromeErrorResponse // a ref to the error response to be used to generate the user friendly error string 81 | } 82 | 83 | // user friendly error response 84 | func (cerr *ChromeRequestErr) Error() string { 85 | return "request " + strconv.FormatInt(cerr.Resp.Id, 10) + " failed, code: " + strconv.FormatInt(cerr.Resp.Error.Code, 10) + " msg: " + cerr.Resp.Error.Message 86 | } 87 | 88 | // When a ChromeTarget crashes and we have to close response channels and return nil 89 | type ChromeEmptyResponseErr struct { 90 | } 91 | 92 | func (cerr *ChromeEmptyResponseErr) Error() string { 93 | return "nil response received" 94 | } 95 | 96 | type ChromeApiTimeoutErr struct { 97 | } 98 | 99 | func (cerr *ChromeApiTimeoutErr) Error() string { 100 | return "timed out waiting for response from chrome" 101 | } 102 | 103 | type ChromeDoneErr struct { 104 | } 105 | 106 | func (cerr *ChromeDoneErr) Error() string { 107 | return "tab is shutting down" 108 | } 109 | 110 | type ChromeCtxDoneErr struct { 111 | } 112 | 113 | func (cerr *ChromeCtxDoneErr) Error() string { 114 | return "context.Context done" 115 | } 116 | 117 | // default request object that has parameters. 118 | type ParamRequest struct { 119 | Id int64 `json:"id"` 120 | Method string `json:"method"` 121 | Params interface{} `json:"params,omitempty"` 122 | } 123 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wirepair/gcd/v2 2 | 3 | go 1.19 4 | 5 | require github.com/goccy/go-json v0.10.2 6 | -------------------------------------------------------------------------------- /v2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 2 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 3 | -------------------------------------------------------------------------------- /v2/logger.go: -------------------------------------------------------------------------------- 1 | package gcd 2 | 3 | import "fmt" 4 | 5 | type Log interface { 6 | Println(args ...interface{}) 7 | } 8 | 9 | type LogDiscarder struct{} 10 | 11 | func (l LogDiscarder) Println(args ...interface{}) {} 12 | 13 | type DebugLogger struct{} 14 | 15 | func (l DebugLogger) Println(args ...interface{}) { 16 | fmt.Println(args...) 17 | } 18 | -------------------------------------------------------------------------------- /v2/observer/message_observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "github.com/wirepair/gcd/v2/gcdmessage" 4 | 5 | type MessageObserver interface { 6 | Request(ID int64, method string, jsonData []byte) 7 | Response(ID int64, method string, jsonData []byte, err error) 8 | Event(method string, data []byte) 9 | } 10 | 11 | // DigResponseData returns the response data if there is any 12 | func DigResponseData(response *gcdmessage.Message) []byte { 13 | if response == nil { 14 | return nil 15 | } 16 | 17 | return response.Data 18 | } 19 | 20 | func NewIgnoreMessagesObserver() *IgnoreMessagesObserver { 21 | return &IgnoreMessagesObserver{} 22 | } 23 | 24 | type IgnoreMessagesObserver struct {} 25 | 26 | func (observer *IgnoreMessagesObserver) Request(ID int64, method string, jsonData []byte) { 27 | // intentionally blank 28 | } 29 | 30 | func (observer *IgnoreMessagesObserver) Response(ID int64, method string, jsonData []byte, err error) { 31 | // intentionally blank 32 | } 33 | 34 | func (observer *IgnoreMessagesObserver) Event(method string, jsonData []byte) { 35 | // intentionally blank 36 | } 37 | -------------------------------------------------------------------------------- /v2/testdata/console_log.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | console log 6 | 11 | 12 | 13 |
console log
14 | 15 | -------------------------------------------------------------------------------- /v2/testdata/cookie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 |
set cookie
18 | 19 | 20 | -------------------------------------------------------------------------------- /v2/testdata/extension/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /v2/testdata/extension/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.0.6 2 | - Fix the extension not working for https://web.whatsapp.com 3 | 4 | 1.0.5 5 | - Fix manifest description being too long from v1.0.4 6 | 7 | 1.0.4 8 | - CSP headers are enabled by default when you install this extension. You must 9 | click the extention's button to disable CSP. 10 | 11 | 1.0.3 12 | - Fixes bad extension packaging of 1.0.2. Do not use 1.0.2. 13 | 14 | 1.0.2 15 | - Make the extension work for iframes. 16 | 17 | 1.0.1 18 | - Initial version. 19 | -------------------------------------------------------------------------------- /v2/testdata/extension/README.md: -------------------------------------------------------------------------------- 1 | Disable Content-Security-Policy in Chromium browers for web application testing. 2 | 3 | [Install via the Chrome Web Store](https://chrome.google.com/webstore/detail/disable-content-security/ieelmcmcagommplceebfedjlakkhpden) 4 | 5 | ## Contributors 6 | 7 | * [Phil Grayson](https://github.com/PhilGrayson) 8 | * [Denis Gorbachev](https://github.com/DenisGorbachev) 9 | -------------------------------------------------------------------------------- /v2/testdata/extension/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Disable Content-Security-Policy for web application testing. When the icon is colored, CSP headers are disabled.", 4 | "description": "The description of this extention" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /v2/testdata/extension/_locales/en_GB/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extDescription": { 3 | "message": "Disable Content-Security-Policy for web application testing. When the icon is coloured, CSP headers are disabled.", 4 | "description": "The description of this extention" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /v2/testdata/extension/background.js: -------------------------------------------------------------------------------- 1 | var isCSPDisabled = false; 2 | 3 | var onHeadersReceived = function(details) { 4 | if (!isCSPDisabled) { 5 | return; 6 | } 7 | 8 | for (var i = 0; i < details.responseHeaders.length; i++) { 9 | if ('content-security-policy' === details.responseHeaders[i].name.toLowerCase()) { 10 | details.responseHeaders[i].value = ''; 11 | } 12 | } 13 | 14 | return { 15 | responseHeaders: details.responseHeaders 16 | }; 17 | }; 18 | 19 | var updateUI = function() { 20 | var iconName = isCSPDisabled ? 'on' : 'off'; 21 | var title = isCSPDisabled ? 'disabled' : 'enabled'; 22 | 23 | chrome.browserAction.setIcon({ path: "images/icon38-" + iconName + ".png" }); 24 | chrome.browserAction.setTitle({ title: 'Content-Security-Policy headers are ' + title }); 25 | }; 26 | 27 | var filter = { 28 | urls: ["*://*/*"], 29 | types: ["main_frame", "sub_frame"] 30 | }; 31 | 32 | chrome.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, ["blocking", "responseHeaders"]); 33 | 34 | chrome.browserAction.onClicked.addListener(function() { 35 | isCSPDisabled = !isCSPDisabled; 36 | 37 | if (isCSPDisabled) { 38 | chrome.browsingData.remove({}, {"serviceWorkers": true}, function () {}); 39 | } 40 | 41 | updateUI() 42 | }); 43 | 44 | updateUI(); 45 | -------------------------------------------------------------------------------- /v2/testdata/extension/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/v2/testdata/extension/images/icon128.png -------------------------------------------------------------------------------- /v2/testdata/extension/images/icon38-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/v2/testdata/extension/images/icon38-off.png -------------------------------------------------------------------------------- /v2/testdata/extension/images/icon38-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/v2/testdata/extension/images/icon38-on.png -------------------------------------------------------------------------------- /v2/testdata/extension/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wirepair/gcd/0c826738782a152e8d580bb0b09dc9524f212669/v2/testdata/extension/images/icon48.png -------------------------------------------------------------------------------- /v2/testdata/extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Disable Content-Security-Policy", 3 | "default_locale": "en", 4 | "description": "__MSG_extDescription__", 5 | "version": "1.0.6", 6 | "author": "Phil Grayson", 7 | "homepage_url": "https://github.com/PhilGrayson/chrome-csp-disable", 8 | "manifest_version": 2, 9 | "permissions": [ 10 | "webRequest", 11 | "webRequestBlocking", 12 | "browsingData", 13 | "http://*/*", 14 | "https://*/*" 15 | ], 16 | "background": { 17 | "scripts": ["background.js"], 18 | "persistent": true 19 | }, 20 | "browser_action": { 21 | "default_title": "Content-Security-Policy headers are enabled", 22 | "default_icon": { 23 | "16": "images/icon38-off.png" 24 | } 25 | }, 26 | "icons": { 27 | "48": "images/icon48.png", 28 | "128": "images/icon128.png" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /v2/wsconn.go: -------------------------------------------------------------------------------- 1 | package gcd 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "crypto/tls" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | ) 14 | 15 | // Use WebSocket from https://github.com/go-rod/rod/blob/master/lib/cdp/websocket.go 16 | 17 | // Dialer interface for WebSocket connection 18 | type Dialer interface { 19 | DialContext(ctx context.Context, network, address string) (net.Conn, error) 20 | } 21 | 22 | // WebSocket client for chromium. It only implements a subset of WebSocket protocol. 23 | // Limitation: https://bugs.chromium.org/p/chromium/issues/detail?id=1069431 24 | // Ref: https://tools.ietf.org/html/rfc6455 25 | type WebSocket struct { 26 | // Dialer is usually used for proxy 27 | Dialer Dialer 28 | 29 | close func() 30 | conn net.Conn 31 | r *bufio.Reader 32 | header [18]byte // Send is thread-safe, so we can safely share a header for all frames 33 | mask []byte 34 | } 35 | 36 | // Connect to browser 37 | func (ws *WebSocket) Connect(ctx context.Context, wsURL string, header http.Header) error { 38 | if ws.conn != nil { 39 | panic("duplicated connection: " + wsURL) 40 | } 41 | 42 | ctx, cancel := context.WithCancel(ctx) 43 | ws.close = cancel 44 | 45 | u, err := url.Parse(wsURL) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | ws.initDialer(u) 51 | 52 | conn, err := ws.Dialer.DialContext(ctx, "tcp", u.Host) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | go func() { 58 | <-ctx.Done() 59 | _ = conn.Close() 60 | }() 61 | 62 | ws.initConstants() 63 | 64 | ws.conn = conn 65 | ws.r = bufio.NewReader(conn) 66 | return ws.handshake(ctx, u, header) 67 | } 68 | 69 | func (ws *WebSocket) initDialer(u *url.URL) { 70 | if ws.Dialer != nil { 71 | return 72 | } 73 | 74 | if u.Scheme == "wss" { 75 | ws.Dialer = &tlsDialer{} 76 | if u.Port() == "" { 77 | u.Host += ":443" 78 | } 79 | } else { 80 | ws.Dialer = &net.Dialer{} 81 | } 82 | } 83 | 84 | func (ws *WebSocket) initConstants() { 85 | // FIN is alway true, Opcode is always text frame. 86 | ws.header = [18]byte{0b1000_0001} 87 | 88 | ws.mask = []byte{0, 1, 2, 3} 89 | } 90 | 91 | // Send a message to browser. 92 | // Because we use zero-copy design, it will modify the content of the msg. 93 | // It won't allocate new memory. 94 | func (ws *WebSocket) Send(msg []byte) error { 95 | ws.header[1] = 0b1000_0000 96 | 97 | size := len(msg) 98 | fieldLen := 0 99 | switch { 100 | case size <= 125: 101 | ws.header[1] |= byte(size) 102 | case size < 65536: 103 | ws.header[1] |= 126 104 | fieldLen = 2 105 | default: 106 | ws.header[1] |= 127 107 | fieldLen = 8 108 | } 109 | 110 | var i int 111 | for i = 0; i < fieldLen; i++ { 112 | digit := (fieldLen - i - 1) * 8 113 | ws.header[i+2] = byte((size >> digit) & 0xff) 114 | } 115 | 116 | copy(ws.header[i+2:], ws.mask) 117 | _, err := ws.conn.Write(ws.header[:i+6]) 118 | if err != nil { 119 | return ws.checkClose(err) 120 | } 121 | 122 | for i := range msg { 123 | msg[i] = msg[i] ^ ws.mask[i%4] 124 | } 125 | 126 | _, err = ws.conn.Write(msg) 127 | return ws.checkClose(err) 128 | } 129 | 130 | // Read a message from browser 131 | func (ws *WebSocket) Read() ([]byte, error) { 132 | _, _ = ws.r.ReadByte() 133 | b, err := ws.r.ReadByte() 134 | if err != nil { 135 | return nil, ws.checkClose(err) 136 | } 137 | 138 | size := 0 139 | fieldLen := 0 140 | 141 | b &= 0x7f 142 | switch { 143 | case b <= 125: 144 | size = int(b) 145 | case b == 126: 146 | fieldLen = 2 147 | case b == 127: 148 | fieldLen = 8 149 | } 150 | 151 | for i := 0; i < fieldLen; i++ { 152 | b, err := ws.r.ReadByte() 153 | if err != nil { 154 | return nil, ws.checkClose(err) 155 | } 156 | 157 | size = size<<8 + int(b) 158 | } 159 | 160 | data := make([]byte, size) 161 | _, err = io.ReadFull(ws.r, data) 162 | return data, ws.checkClose(err) 163 | } 164 | 165 | // ErrBadHandshake type 166 | type ErrBadHandshake struct { 167 | *http.Response 168 | } 169 | 170 | func (e *ErrBadHandshake) Error() string { 171 | body, _ := ioutil.ReadAll(e.Response.Body) 172 | return fmt.Sprintf( 173 | "websocket bad handshake: %s. %s", 174 | e.Response.Status, body, 175 | ) 176 | } 177 | 178 | func (ws *WebSocket) handshake(ctx context.Context, u *url.URL, header http.Header) error { 179 | req := (&http.Request{Method: http.MethodGet, URL: u, Header: http.Header{ 180 | "Upgrade": {"websocket"}, 181 | "Connection": {"Upgrade"}, 182 | "Sec-WebSocket-Key": {"nil"}, 183 | "Sec-WebSocket-Version": {"13"}, 184 | }}).WithContext(ctx) 185 | 186 | for k, vs := range header { 187 | if k == "Host" && len(vs) > 0 { 188 | req.Host = vs[0] 189 | } else { 190 | req.Header[k] = vs 191 | } 192 | } 193 | 194 | err := req.Write(ws.conn) 195 | if err != nil { 196 | return ws.checkClose(err) 197 | } 198 | 199 | res, err := http.ReadResponse(ws.r, req) 200 | if err != nil { 201 | return ws.checkClose(err) 202 | } 203 | 204 | if res.StatusCode != http.StatusSwitchingProtocols || 205 | res.Header.Get("Sec-Websocket-Accept") != "Q67D9eATKx531lK8F7u2rqQNnNI=" { 206 | return &ErrBadHandshake{res} 207 | } 208 | 209 | return nil 210 | } 211 | 212 | func (ws *WebSocket) checkClose(err error) error { 213 | if err != nil { 214 | ws.close() 215 | } 216 | return err 217 | } 218 | 219 | // TODO: replace it with tls.Dialer once golang v1.15 is widely used. 220 | type tlsDialer struct{} 221 | 222 | func (d *tlsDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { 223 | return tls.Dial(network, address, nil) 224 | } 225 | -------------------------------------------------------------------------------- /wsconn.go: -------------------------------------------------------------------------------- 1 | package gcd 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "io" 8 | "net" 9 | "net/url" 10 | "time" 11 | 12 | "github.com/gobwas/ws" 13 | "github.com/gobwas/ws/wsutil" 14 | ) 15 | 16 | const writeSize = 15000000 // 15mb 17 | 18 | // adapted from https://github.com/chromedp/chromedp/blob/8e0a16689423d48d8907c62a543c7ea468059228/conn.go 19 | type wsConn struct { 20 | conn net.Conn 21 | writer wsutil.Writer 22 | } 23 | 24 | func newWsConnDial(ctx context.Context, url string) (*wsConn, error) { 25 | wconn := &wsConn{} 26 | conn, br, _, err := ws.Dial(ctx, url) 27 | if err != nil { 28 | return nil, err 29 | } 30 | if br != nil { 31 | panic("br should be nil") 32 | } 33 | wconn.conn = conn 34 | wconn.writer = *wsutil.NewWriterBufferSize(conn, ws.StateClientSide, ws.OpText, writeSize) 35 | return wconn, nil 36 | } 37 | 38 | func formatURL(toFormat string) string { 39 | u, err := url.Parse(toFormat) 40 | if err != nil { 41 | return "" 42 | } 43 | host, port, err := net.SplitHostPort(u.Host) 44 | if err != nil { 45 | return "" 46 | } 47 | addr, err := net.ResolveIPAddr("ip", host) 48 | if err != nil { 49 | return "" 50 | } 51 | u.Host = net.JoinHostPort(addr.IP.String(), port) 52 | return u.String() 53 | } 54 | 55 | func (c *wsConn) Read(ctx context.Context, msg *[]byte) error { 56 | // get websocket reader 57 | c.conn.SetReadDeadline(time.Now().Add(10 * time.Second)) 58 | reader := wsutil.NewReader(c.conn, ws.StateClientSide) 59 | h, err := reader.NextFrame() 60 | if err != nil { 61 | return err 62 | } 63 | 64 | if h.OpCode == ws.OpClose { 65 | return io.EOF 66 | } 67 | 68 | if h.OpCode != ws.OpText { 69 | return fmt.Errorf("InvalidWebsocketMessage") 70 | } 71 | 72 | var b bytes.Buffer 73 | if _, err := b.ReadFrom(reader); err != nil { 74 | return err 75 | } 76 | 77 | *msg = b.Bytes() 78 | return nil 79 | } 80 | 81 | // Write writes a message. 82 | func (c *wsConn) Write(_ context.Context, msg []byte) error { 83 | c.writer.Reset(c.conn, ws.StateClientSide, ws.OpText) 84 | if _, err := c.writer.Write(msg); err != nil { 85 | return err 86 | } 87 | return c.writer.Flush() 88 | } 89 | 90 | func (c *wsConn) Close() error { 91 | return c.conn.Close() 92 | } 93 | --------------------------------------------------------------------------------