Browse Source

Backport (1-7-x) - Set appropriate defaults for webview options (#12294)

* Fix NIB.

* Fix up tests
Samuel Attard 7 years ago
parent
commit
1a48ee2827

+ 9 - 0
atom/browser/api/atom_api_web_contents.cc

@@ -1721,6 +1721,14 @@ v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
   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() {
   if (owner_window())
     return Window::From(isolate(), owner_window());
@@ -1851,6 +1859,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("_getZoomFactor", &WebContents::GetZoomFactor)
       .SetMethod("getType", &WebContents::GetType)
       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
+      .SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences)
       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
       .SetMethod("unregisterServiceWorker",

+ 1 - 0
atom/browser/api/atom_api_web_contents.h

@@ -210,6 +210,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
 
   // Returns the web preferences of current WebContents.
   v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate);
+  v8::Local<v8::Value> GetLastWebPreferences(v8::Isolate* isolate);
 
   // Returns the owner window.
   v8::Local<v8::Value> GetOwnerBrowserWindow();

+ 38 - 0
atom/browser/web_contents_preferences.cc

@@ -47,6 +47,28 @@ WebContentsPreferences::WebContentsPreferences(
   web_contents->SetUserData(UserDataKey(), 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() {
@@ -55,6 +77,16 @@ WebContentsPreferences::~WebContentsPreferences() {
       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) {
   web_preferences_.MergeDictionary(&extend);
 }
@@ -79,6 +111,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
 
   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;
   // Check if plugins are enabled.
   if (web_preferences.GetBoolean("plugins", &b) && b)

+ 7 - 0
atom/browser/web_contents_preferences.h

@@ -57,6 +57,9 @@ class WebContentsPreferences
 
   // Returns the web preferences.
   base::DictionaryValue* web_preferences() { return &web_preferences_; }
+  base::DictionaryValue* last_web_preferences() {
+    return &last_web_preferences_;
+  }
 
  private:
   friend class content::WebContentsUserData<WebContentsPreferences>;
@@ -65,6 +68,10 @@ class WebContentsPreferences
 
   content::WebContents* web_contents_;
   base::DictionaryValue web_preferences_;
+  base::DictionaryValue last_web_preferences_;
+
+  // 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
   bool GetInteger(const std::string& attributeName, int* intValue);

+ 2 - 2
lib/browser/guest-view-manager.js

@@ -155,7 +155,7 @@ const createGuest = function (embedder, params) {
   // Forward internal web contents event to embedder to handle
   // native window.open setup
   guest.on('-add-new-contents', (...args) => {
-    if (guest.getWebPreferences().nativeWindowOpen === true) {
+    if (guest.getLastWebPreferences().nativeWindowOpen === true) {
       const embedder = getEmbedder(guestInstanceId)
       if (embedder != null) {
         embedder.emit('-add-new-contents', ...args)
@@ -163,7 +163,7 @@ const createGuest = function (embedder, params) {
     }
   })
   guest.on('-web-contents-created', (...args) => {
-    if (guest.getWebPreferences().nativeWindowOpen === true) {
+    if (guest.getLastWebPreferences().nativeWindowOpen === true) {
       const embedder = getEmbedder(guestInstanceId)
       if (embedder != null) {
         embedder.emit('-web-contents-created', ...args)

+ 4 - 4
lib/browser/guest-window-manager.js

@@ -50,12 +50,12 @@ const mergeBrowserWindowOptions = function (embedder, options) {
     mergeOptions(options, embedder.browserWindowOptions)
   } else {
     // 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
   for (const [name, value] of inheritedWebPreferences) {
-    if (embedder.getWebPreferences()[name] === value) {
+    if (embedder.getLastWebPreferences()[name] === value) {
       options.webPreferences[name] = value
     }
   }
@@ -168,8 +168,8 @@ const getGuestWindow = function (guestContents) {
 // The W3C does not have anything on this, but from my understanding of the
 // security model of |window.opener|, this should be fine.
 const canAccessWindow = function (sender, target) {
-  return (target.getWebPreferences().openerId === sender.id) ||
-         (sender.getWebPreferences().nodeIntegration === true) ||
+  return (target.getLastWebPreferences().openerId === sender.id) ||
+         (sender.getLastWebPreferences().nodeIntegration === true) ||
          isSameOrigin(sender.getURL(), target.getURL())
 }
 

+ 3 - 4
spec/api-browser-window-spec.js

@@ -2691,10 +2691,9 @@ describe('BrowserWindow module', function () {
       })
       w.loadURL('file://' + fixtures + '/api/isolated.html')
     })
-
-    it('enables context isolation on child windows', function (done) {
-      app.once('browser-window-created', function (event, window) {
-        assert.equal(window.webContents.getWebPreferences().contextIsolation, true)
+    it('enables context isolation on child windows', (done) => {
+      app.once('browser-window-created', (event, window) => {
+        assert.equal(window.webContents.getLastWebPreferences().contextIsolation, true)
         done()
       })
       w.loadURL('file://' + fixtures + '/pages/window-open.html')

+ 25 - 5
spec/chromium-spec.js

@@ -241,8 +241,28 @@ describe('chromium feature', function () {
       b = window.open(windowUrl, '', 'nodeIntegration=no,show=no')
     })
 
-    it('disables node integration when it is disabled on the parent window for chrome devtools URLs', function (done) {
-      var b
+    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) => {
+      let b
       app.once('web-contents-created', (event, contents) => {
         contents.once('did-finish-load', () => {
           contents.executeJavaScript('typeof process').then((typeofProcessGlobal) => {
@@ -260,7 +280,7 @@ describe('chromium feature', function () {
       app.once('web-contents-created', (event, contents) => {
         contents.once('did-finish-load', () => {
           app.once('browser-window-created', (event, window) => {
-            const preferences = window.webContents.getWebPreferences()
+            const preferences = window.webContents.getLastWebPreferences()
             assert.equal(preferences.javascript, false)
             window.destroy()
             b.close()
@@ -486,7 +506,7 @@ describe('chromium feature', function () {
         done()
       }
       window.addEventListener('message', listener)
-      w = window.open(url, '', 'show=no')
+      w = window.open(url, '', 'show=no,nodeIntegration=no')
     })
 
     it('works when origin matches', function (done) {
@@ -495,7 +515,7 @@ describe('chromium feature', function () {
         done()
       }
       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', function (done) {

+ 15 - 0
spec/fixtures/pages/window-opener-no-web-view-tag.html

@@ -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 - 0
spec/fixtures/pages/window-opener-web-view.html

@@ -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>