1package cef
2
3import (
4 "testing"
5 "unsafe"
6
7 purecef "github.com/bnema/purego-cef/cef"
8 cefmocks "github.com/bnema/purego-cef/cef/mocks"
9 "github.com/stretchr/testify/require"
10)
11
12type testPopupOpenerDictionaryValue struct {
13 strings map[string]string
14 bools map[string]int32
15}
16
17func (v testPopupOpenerDictionaryValue) IsValid() bool { return true }
18func (v testPopupOpenerDictionaryValue) IsOwned() bool { return true }
19func (v testPopupOpenerDictionaryValue) IsReadOnly() bool { return false }
20func (v testPopupOpenerDictionaryValue) IsSame(purecef.DictionaryValue) bool { return false }
21func (v testPopupOpenerDictionaryValue) IsEqual(purecef.DictionaryValue) bool { return false }
22func (v testPopupOpenerDictionaryValue) Copy(int32) purecef.DictionaryValue { return v }
23func (v testPopupOpenerDictionaryValue) GetSize() int { return len(v.strings) + len(v.bools) }
24func (v testPopupOpenerDictionaryValue) Clear() int32 { return 1 }
25func (v testPopupOpenerDictionaryValue) HasKey(key string) bool {
26 _, ok := v.strings[key]
27 if ok {
28 return true
29 }
30 _, ok = v.bools[key]
31 return ok
32}
33func (v testPopupOpenerDictionaryValue) GetKeys(purecef.StringList) int32 { return 0 }
34func (v testPopupOpenerDictionaryValue) Remove(string) int32 { return 1 }
35func (v testPopupOpenerDictionaryValue) GetType(string) purecef.ValueType { return 0 }
36func (v testPopupOpenerDictionaryValue) GetValue(string) purecef.Value { return nil }
37func (v testPopupOpenerDictionaryValue) GetBool(key string) int32 { return v.bools[key] }
38func (v testPopupOpenerDictionaryValue) GetInt(string) int32 { return 0 }
39func (v testPopupOpenerDictionaryValue) GetDouble(string) float64 { return 0 }
40func (v testPopupOpenerDictionaryValue) GetString(key string) string { return v.strings[key] }
41func (v testPopupOpenerDictionaryValue) GetBinary(string) purecef.BinaryValue { return nil }
42func (v testPopupOpenerDictionaryValue) GetDictionary(string) purecef.DictionaryValue { return nil }
43func (v testPopupOpenerDictionaryValue) GetList(string) purecef.ListValue { return nil }
44func (v testPopupOpenerDictionaryValue) SetValue(string, purecef.Value) int32 { return 1 }
45func (v testPopupOpenerDictionaryValue) SetNull(string) int32 { return 1 }
46func (v testPopupOpenerDictionaryValue) SetBool(string, int32) int32 { return 1 }
47func (v testPopupOpenerDictionaryValue) SetInt(string, int32) int32 { return 1 }
48func (v testPopupOpenerDictionaryValue) SetDouble(string, float64) int32 { return 1 }
49func (v testPopupOpenerDictionaryValue) SetString(string, string) int32 { return 1 }
50func (v testPopupOpenerDictionaryValue) SetBinary(string, purecef.BinaryValue) int32 { return 1 }
51func (v testPopupOpenerDictionaryValue) SetDictionary(string, purecef.DictionaryValue) int32 {
52 return 1
53}
54func (v testPopupOpenerDictionaryValue) SetList(string, purecef.ListValue) int32 { return 1 }
55
56type testPopupOpenerV8Context struct {
57 valid bool
58 evalCode string
59 evalURL string
60 enterCalls int
61 exitCalls int
62}
63
64func (c *testPopupOpenerV8Context) GetTaskRunner() purecef.TaskRunner { return nil }
65func (c *testPopupOpenerV8Context) IsValid() bool { return c.valid }
66func (c *testPopupOpenerV8Context) GetBrowser() purecef.Browser { return nil }
67func (c *testPopupOpenerV8Context) GetFrame() purecef.Frame { return nil }
68func (c *testPopupOpenerV8Context) GetGlobal() purecef.V8Value { return nil }
69func (c *testPopupOpenerV8Context) Enter() int32 {
70 c.enterCalls++
71 return 1
72}
73func (c *testPopupOpenerV8Context) Exit() int32 {
74 c.exitCalls++
75 return 1
76}
77func (c *testPopupOpenerV8Context) IsSame(that purecef.V8Context) bool { return c == that }
78func (c *testPopupOpenerV8Context) Eval(code, scriptURL string, _ int32, _, _ unsafe.Pointer) int32 {
79 c.evalCode = code
80 c.evalURL = scriptURL
81 return 1
82}
83func (c *testPopupOpenerV8Context) RawPointer() unsafe.Pointer { return nil }
84func (c *testPopupOpenerV8Context) Release() {}
85
86func TestDecodePopupOpenerRenderMetadata(t *testing.T) {
87 metadata, ok := decodePopupOpenerRenderMetadata(testPopupOpenerDictionaryValue{
88 strings: map[string]string{
89 popupOpenerExtraInfoParentURIKey: "https://example.com/login",
90 popupOpenerExtraInfoBridgeNonceKey: "bridge-nonce",
91 },
92 bools: map[string]int32{
93 popupOpenerExtraInfoEnabledKey: 1,
94 },
95 })
96
97 require.True(t, ok)
98 require.Equal(t, "https://example.com/login", metadata.ParentURI)
99 require.Equal(t, "bridge-nonce", metadata.BridgeNonce)
100}
101
102func TestPopupOpenerRenderProcessHandler_OnContextCreatedInstallsBridgeForConfiguredMainFrame(t *testing.T) {
103 h := newPopupOpenerRenderProcessHandler()
104 browser := cefmocks.NewMockBrowser(t)
105 browser.EXPECT().GetIdentifier().Return(int32(42)).Twice()
106 ctx := &testPopupOpenerV8Context{valid: true}
107
108 h.OnBrowserCreated(browser, testPopupOpenerDictionaryValue{
109 strings: map[string]string{
110 popupOpenerExtraInfoParentURIKey: "https://example.com/login",
111 popupOpenerExtraInfoBridgeNonceKey: "bridge-nonce",
112 },
113 bools: map[string]int32{
114 popupOpenerExtraInfoEnabledKey: 1,
115 },
116 })
117 h.OnContextCreated(browser, stubFrame{main: true, url: "https://example.com/popup"}, ctx)
118
119 require.Equal(t, 1, ctx.enterCalls)
120 require.Equal(t, 1, ctx.exitCalls)
121 require.Contains(t, ctx.evalCode, "popup-opener-navigate")
122 require.Contains(t, ctx.evalCode, "popup-opener-post-message")
123 require.Contains(t, ctx.evalCode, "https://example.com/login")
124 require.Contains(t, ctx.evalCode, "bridge-nonce")
125 require.Equal(t, popupOpenerRenderScriptURL, ctx.evalURL)
126}
127
128func TestPopupOpenerRenderProcessHandler_OnContextCreatedSkipsWithoutMetadata(t *testing.T) {
129 h := newPopupOpenerRenderProcessHandler()
130 browser := cefmocks.NewMockBrowser(t)
131 browser.EXPECT().GetIdentifier().Return(int32(7)).Once()
132 ctx := &testPopupOpenerV8Context{valid: true}
133
134 h.OnContextCreated(browser, stubFrame{main: true, url: "https://example.com/popup"}, ctx)
135
136 require.Zero(t, ctx.enterCalls)
137 require.Empty(t, ctx.evalCode)
138}
139
140func TestEnablePopupOpenerBridge_SyncsPendingCreateExtraInfo(t *testing.T) {
141 oldBuilder := popupOpenerRenderExtraInfoBuilder
142 popupOpenerRenderExtraInfoBuilder = func(parentURI, bridgeNonce string) purecef.DictionaryValue {
143 require.Equal(t, "https://example.com/login", parentURI)
144 require.NotEmpty(t, bridgeNonce)
145 return testPopupOpenerDictionaryValue{strings: map[string]string{"ok": "1"}}
146 }
147 t.Cleanup(func() {
148 popupOpenerRenderExtraInfoBuilder = oldBuilder
149 })
150
151 parent := &WebView{}
152 parent.updateURI("https://example.com/login")
153 windowInfo := purecef.NewWindowInfo()
154 wv := &WebView{pendingCreate: &pendingBrowserCreate{windowInfo: &windowInfo}}
155
156 wv.EnablePopupOpenerBridge(parent, false)
157
158 require.NotNil(t, wv.pendingCreate.extraInfo)
159 require.NotEmpty(t, wv.bridgeNonce)
160}