Set appropriate defaults for webview options (#12271)
* Persist defaults to webPreferences object to JS land can read the inferred values instead of just user defined values * Test inherited default propogation * Refactor to remove coupling from fetching values and defaults * Test description type * Fix up tests
This commit is contained in:
		
					parent
					
						
							
								f54c94d6c9
							
						
					
				
			
			
				commit
				
					
						c2673aa970
					
				
			
		
					 11 changed files with 110 additions and 11 deletions
				
			
		|  | @ -1825,6 +1825,14 @@ v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) { | ||||||
|   return mate::ConvertToV8(isolate, *web_preferences->web_preferences()); |   return mate::ConvertToV8(isolate, *web_preferences->web_preferences()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | v8::Local<v8::Value> WebContents::GetLastWebPreferences(v8::Isolate* isolate) { | ||||||
|  |   WebContentsPreferences* web_preferences = | ||||||
|  |       WebContentsPreferences::FromWebContents(web_contents()); | ||||||
|  |   if (!web_preferences) | ||||||
|  |     return v8::Null(isolate); | ||||||
|  |   return mate::ConvertToV8(isolate, *web_preferences->last_web_preferences()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() { | v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() { | ||||||
|   if (owner_window()) |   if (owner_window()) | ||||||
|     return BrowserWindow::From(isolate(), owner_window()); |     return BrowserWindow::From(isolate(), owner_window()); | ||||||
|  | @ -1976,6 +1984,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, | ||||||
|       .SetMethod("_getZoomFactor", &WebContents::GetZoomFactor) |       .SetMethod("_getZoomFactor", &WebContents::GetZoomFactor) | ||||||
|       .SetMethod("getType", &WebContents::GetType) |       .SetMethod("getType", &WebContents::GetType) | ||||||
|       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) |       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) | ||||||
|  |       .SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences) | ||||||
|       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) |       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) | ||||||
|       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) |       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) | ||||||
|       .SetMethod("unregisterServiceWorker", |       .SetMethod("unregisterServiceWorker", | ||||||
|  |  | ||||||
|  | @ -224,6 +224,7 @@ class WebContents : public mate::TrackableObject<WebContents>, | ||||||
| 
 | 
 | ||||||
|   // Returns the web preferences of current WebContents.
 |   // Returns the web preferences of current WebContents.
 | ||||||
|   v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate); |   v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate); | ||||||
|  |   v8::Local<v8::Value> GetLastWebPreferences(v8::Isolate* isolate); | ||||||
| 
 | 
 | ||||||
|   // Returns the owner window.
 |   // Returns the owner window.
 | ||||||
|   v8::Local<v8::Value> GetOwnerBrowserWindow(); |   v8::Local<v8::Value> GetOwnerBrowserWindow(); | ||||||
|  |  | ||||||
|  | @ -48,6 +48,28 @@ WebContentsPreferences::WebContentsPreferences( | ||||||
|   web_contents->SetUserData(UserDataKey(), base::WrapUnique(this)); |   web_contents->SetUserData(UserDataKey(), base::WrapUnique(this)); | ||||||
| 
 | 
 | ||||||
|   instances_.push_back(this); |   instances_.push_back(this); | ||||||
|  | 
 | ||||||
|  |   // Set WebPreferences defaults onto the JS object
 | ||||||
|  |   SetDefaultBoolIfUndefined("plugins", false); | ||||||
|  |   SetDefaultBoolIfUndefined(options::kExperimentalFeatures, false); | ||||||
|  |   SetDefaultBoolIfUndefined(options::kExperimentalCanvasFeatures, false); | ||||||
|  |   bool node = SetDefaultBoolIfUndefined(options::kNodeIntegration, true); | ||||||
|  |   SetDefaultBoolIfUndefined(options::kNodeIntegrationInWorker, false); | ||||||
|  |   SetDefaultBoolIfUndefined(options::kWebviewTag, node); | ||||||
|  |   SetDefaultBoolIfUndefined("sandbox", false); | ||||||
|  |   SetDefaultBoolIfUndefined("nativeWindowOpen", false); | ||||||
|  |   SetDefaultBoolIfUndefined(options::kContextIsolation, false); | ||||||
|  |   SetDefaultBoolIfUndefined("javascript", true); | ||||||
|  |   SetDefaultBoolIfUndefined("images", true); | ||||||
|  |   SetDefaultBoolIfUndefined("textAreasAreResizable", true); | ||||||
|  |   SetDefaultBoolIfUndefined("webgl", true); | ||||||
|  |   SetDefaultBoolIfUndefined("webSecurity", true); | ||||||
|  |   SetDefaultBoolIfUndefined("allowRunningInsecureContent", false); | ||||||
|  |   #if defined(OS_MACOSX) | ||||||
|  |   SetDefaultBoolIfUndefined(options::kScrollBounce, false); | ||||||
|  |   #endif | ||||||
|  |   SetDefaultBoolIfUndefined("offscreen", false); | ||||||
|  |   last_web_preferences_.MergeDictionary(&web_preferences_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| WebContentsPreferences::~WebContentsPreferences() { | WebContentsPreferences::~WebContentsPreferences() { | ||||||
|  | @ -56,6 +78,16 @@ WebContentsPreferences::~WebContentsPreferences() { | ||||||
|       instances_.end()); |       instances_.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool WebContentsPreferences::SetDefaultBoolIfUndefined(const std::string key, | ||||||
|  |                                                        bool val) { | ||||||
|  |   bool existing; | ||||||
|  |   if (!web_preferences_.GetBoolean(key, &existing)) { | ||||||
|  |     web_preferences_.SetBoolean(key, val); | ||||||
|  |     return val; | ||||||
|  |   } | ||||||
|  |   return existing; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void WebContentsPreferences::Merge(const base::DictionaryValue& extend) { | void WebContentsPreferences::Merge(const base::DictionaryValue& extend) { | ||||||
|   web_preferences_.MergeDictionary(&extend); |   web_preferences_.MergeDictionary(&extend); | ||||||
| } | } | ||||||
|  | @ -80,6 +112,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( | ||||||
| 
 | 
 | ||||||
|   base::DictionaryValue& web_preferences = self->web_preferences_; |   base::DictionaryValue& web_preferences = self->web_preferences_; | ||||||
| 
 | 
 | ||||||
|  |   // We are appending args to a webContents so let's save the current state
 | ||||||
|  |   // of our preferences object so that during the lifetime of the WebContents
 | ||||||
|  |   // we can fetch the options used to initally configure the WebContents
 | ||||||
|  |   self->last_web_preferences_.Clear(); | ||||||
|  |   self->last_web_preferences_.MergeDictionary(&web_preferences); | ||||||
|  | 
 | ||||||
|   bool b; |   bool b; | ||||||
|   // Check if plugins are enabled.
 |   // Check if plugins are enabled.
 | ||||||
|   if (web_preferences.GetBoolean("plugins", &b) && b) |   if (web_preferences.GetBoolean("plugins", &b) && b) | ||||||
|  |  | ||||||
|  | @ -57,10 +57,16 @@ class WebContentsPreferences | ||||||
| 
 | 
 | ||||||
|   // Returns the web preferences.
 |   // Returns the web preferences.
 | ||||||
|   base::DictionaryValue* web_preferences() { return &web_preferences_; } |   base::DictionaryValue* web_preferences() { return &web_preferences_; } | ||||||
|  |   base::DictionaryValue* last_web_preferences() { | ||||||
|  |     return &last_web_preferences_; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   friend class content::WebContentsUserData<WebContentsPreferences>; |   friend class content::WebContentsUserData<WebContentsPreferences>; | ||||||
| 
 | 
 | ||||||
|  |   // Set preference value to given bool if user did not provide value
 | ||||||
|  |   bool SetDefaultBoolIfUndefined(const std::string key, bool val); | ||||||
|  | 
 | ||||||
|   // Get preferences value as integer possibly coercing it from a string
 |   // Get preferences value as integer possibly coercing it from a string
 | ||||||
|   bool GetInteger(const std::string& attributeName, int* intValue); |   bool GetInteger(const std::string& attributeName, int* intValue); | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +74,7 @@ class WebContentsPreferences | ||||||
| 
 | 
 | ||||||
|   content::WebContents* web_contents_; |   content::WebContents* web_contents_; | ||||||
|   base::DictionaryValue web_preferences_; |   base::DictionaryValue web_preferences_; | ||||||
|  |   base::DictionaryValue last_web_preferences_; | ||||||
| 
 | 
 | ||||||
|   DISALLOW_COPY_AND_ASSIGN(WebContentsPreferences); |   DISALLOW_COPY_AND_ASSIGN(WebContentsPreferences); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -156,7 +156,7 @@ const createGuest = function (embedder, params) { | ||||||
|   // Forward internal web contents event to embedder to handle
 |   // Forward internal web contents event to embedder to handle
 | ||||||
|   // native window.open setup
 |   // native window.open setup
 | ||||||
|   guest.on('-add-new-contents', (...args) => { |   guest.on('-add-new-contents', (...args) => { | ||||||
|     if (guest.getWebPreferences().nativeWindowOpen === true) { |     if (guest.getLastWebPreferences().nativeWindowOpen === true) { | ||||||
|       const embedder = getEmbedder(guestInstanceId) |       const embedder = getEmbedder(guestInstanceId) | ||||||
|       if (embedder != null) { |       if (embedder != null) { | ||||||
|         embedder.emit('-add-new-contents', ...args) |         embedder.emit('-add-new-contents', ...args) | ||||||
|  | @ -164,7 +164,7 @@ const createGuest = function (embedder, params) { | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
|   guest.on('-web-contents-created', (...args) => { |   guest.on('-web-contents-created', (...args) => { | ||||||
|     if (guest.getWebPreferences().nativeWindowOpen === true) { |     if (guest.getLastWebPreferences().nativeWindowOpen === true) { | ||||||
|       const embedder = getEmbedder(guestInstanceId) |       const embedder = getEmbedder(guestInstanceId) | ||||||
|       if (embedder != null) { |       if (embedder != null) { | ||||||
|         embedder.emit('-web-contents-created', ...args) |         embedder.emit('-web-contents-created', ...args) | ||||||
|  |  | ||||||
|  | @ -59,12 +59,12 @@ const mergeBrowserWindowOptions = function (embedder, options) { | ||||||
|     mergeOptions(options, parentOptions) |     mergeOptions(options, parentOptions) | ||||||
|   } else { |   } else { | ||||||
|     // Or only inherit webPreferences if it is a webview.
 |     // Or only inherit webPreferences if it is a webview.
 | ||||||
|     mergeOptions(options.webPreferences, embedder.getWebPreferences()) |     mergeOptions(options.webPreferences, embedder.getLastWebPreferences()) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Inherit certain option values from parent window
 |   // Inherit certain option values from parent window
 | ||||||
|   for (const [name, value] of inheritedWebPreferences) { |   for (const [name, value] of inheritedWebPreferences) { | ||||||
|     if (embedder.getWebPreferences()[name] === value) { |     if (embedder.getLastWebPreferences()[name] === value) { | ||||||
|       options.webPreferences[name] = value |       options.webPreferences[name] = value | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -177,8 +177,8 @@ const getGuestWindow = function (guestContents) { | ||||||
| // The W3C does not have anything on this, but from my understanding of the
 | // The W3C does not have anything on this, but from my understanding of the
 | ||||||
| // security model of |window.opener|, this should be fine.
 | // security model of |window.opener|, this should be fine.
 | ||||||
| const canAccessWindow = function (sender, target) { | const canAccessWindow = function (sender, target) { | ||||||
|   return (target.getWebPreferences().openerId === sender.id) || |   return (target.getLastWebPreferences().openerId === sender.id) || | ||||||
|          (sender.getWebPreferences().nodeIntegration === true) || |          (sender.getLastWebPreferences().nodeIntegration === true) || | ||||||
|          isSameOrigin(sender.getURL(), target.getURL()) |          isSameOrigin(sender.getURL(), target.getURL()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -84,7 +84,7 @@ const getWebPreferences = function () { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const { remote } = require('electron') |     const { remote } = require('electron') | ||||||
|     webPreferences = remote.getCurrentWebContents().getWebPreferences() |     webPreferences = remote.getCurrentWebContents().getLastWebPreferences() | ||||||
|     return webPreferences |     return webPreferences | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     return null |     return null | ||||||
|  |  | ||||||
|  | @ -2999,7 +2999,7 @@ describe('BrowserWindow module', () => { | ||||||
|     }) |     }) | ||||||
|     it('enables context isolation on child windows', (done) => { |     it('enables context isolation on child windows', (done) => { | ||||||
|       app.once('browser-window-created', (event, window) => { |       app.once('browser-window-created', (event, window) => { | ||||||
|         assert.equal(window.webContents.getWebPreferences().contextIsolation, true) |         assert.equal(window.webContents.getLastWebPreferences().contextIsolation, true) | ||||||
|         done() |         done() | ||||||
|       }) |       }) | ||||||
|       w.loadURL(`file://${fixtures}/pages/window-open.html`) |       w.loadURL(`file://${fixtures}/pages/window-open.html`) | ||||||
|  |  | ||||||
|  | @ -268,6 +268,26 @@ describe('chromium feature', () => { | ||||||
|       b = window.open(windowUrl, '', 'nodeIntegration=no,show=no') |       b = window.open(windowUrl, '', 'nodeIntegration=no,show=no') | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|  |     it('disables webviewTag when node integration is disabled on the parent window', (done) => { | ||||||
|  |       let b | ||||||
|  |       listener = (event) => { | ||||||
|  |         assert.equal(event.data.isWebViewUndefined, true) | ||||||
|  |         b.close() | ||||||
|  |         done() | ||||||
|  |       } | ||||||
|  |       window.addEventListener('message', listener) | ||||||
|  | 
 | ||||||
|  |       const windowUrl = require('url').format({ | ||||||
|  |         pathname: `${fixtures}/pages/window-opener-no-web-view-tag.html`, | ||||||
|  |         protocol: 'file', | ||||||
|  |         query: { | ||||||
|  |           p: `${fixtures}/pages/window-opener-web-view.html` | ||||||
|  |         }, | ||||||
|  |         slashes: true | ||||||
|  |       }) | ||||||
|  |       b = window.open(windowUrl, '', 'nodeIntegration=no,show=no') | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|     it('disables node integration when it is disabled on the parent window for chrome devtools URLs', (done) => { |     it('disables node integration when it is disabled on the parent window for chrome devtools URLs', (done) => { | ||||||
|       let b |       let b | ||||||
|       app.once('web-contents-created', (event, contents) => { |       app.once('web-contents-created', (event, contents) => { | ||||||
|  | @ -287,7 +307,7 @@ describe('chromium feature', () => { | ||||||
|       app.once('web-contents-created', (event, contents) => { |       app.once('web-contents-created', (event, contents) => { | ||||||
|         contents.once('did-finish-load', () => { |         contents.once('did-finish-load', () => { | ||||||
|           app.once('browser-window-created', (event, window) => { |           app.once('browser-window-created', (event, window) => { | ||||||
|             const preferences = window.webContents.getWebPreferences() |             const preferences = window.webContents.getLastWebPreferences() | ||||||
|             assert.equal(preferences.javascript, false) |             assert.equal(preferences.javascript, false) | ||||||
|             window.destroy() |             window.destroy() | ||||||
|             b.close() |             b.close() | ||||||
|  | @ -509,7 +529,7 @@ describe('chromium feature', () => { | ||||||
|         done() |         done() | ||||||
|       } |       } | ||||||
|       window.addEventListener('message', listener) |       window.addEventListener('message', listener) | ||||||
|       w = window.open(url, '', 'show=no') |       w = window.open(url, '', 'show=no,nodeIntegration=no') | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|     it('works when origin matches', (done) => { |     it('works when origin matches', (done) => { | ||||||
|  | @ -518,7 +538,7 @@ describe('chromium feature', () => { | ||||||
|         done() |         done() | ||||||
|       } |       } | ||||||
|       window.addEventListener('message', listener) |       window.addEventListener('message', listener) | ||||||
|       w = window.open(`file://${fixtures}/pages/window-opener-location.html`, '', 'show=no') |       w = window.open(`file://${fixtures}/pages/window-opener-location.html`, '', 'show=no,nodeIntegration=no') | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|     it('works when origin does not match opener but has node integration', (done) => { |     it('works when origin does not match opener but has node integration', (done) => { | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								spec/fixtures/pages/window-opener-no-web-view-tag.html
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/fixtures/pages/window-opener-no-web-view-tag.html
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <html> | ||||||
|  | <body> | ||||||
|  | <script type="text/javascript" charset="utf-8"> | ||||||
|  |   var windowUrl = decodeURIComponent(window.location.search.substring(3)) | ||||||
|  |   var opened = window.open('file://' + windowUrl, '', 'webviewTag=yes,show=no') | ||||||
|  |   window.addEventListener('message', function (event) { | ||||||
|  |     try { | ||||||
|  |       opened.close() | ||||||
|  |     } finally { | ||||||
|  |       window.opener.postMessage(event.data, '*') | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | </script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										9
									
								
								spec/fixtures/pages/window-opener-web-view.html
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								spec/fixtures/pages/window-opener-web-view.html
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | <html> | ||||||
|  | <body> | ||||||
|  | <script type="text/javascript" charset="utf-8"> | ||||||
|  |   window.onload = () => { | ||||||
|  |     window.opener.postMessage({isWebViewUndefined: typeof WebView === 'undefined'}, '*') | ||||||
|  |   } | ||||||
|  | </script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Charles Kerr
				Charles Kerr