diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7e4a3c85..8e055f7c72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] +### Fixed +- [#1080](https://github.com/plotly/dash/pull/1080) Handle case where dash fails to load when used inside an iframe with a sandbox attribute that only has allow-scripts + + ## [1.8.0] - 2020-01-14 ### Added - [#1073](https://github.com/plotly/dash/pull/1073) Two new functions to simplify usage handling URLs and pathnames: `app.get_relative_path` & `app.trim_relative_path`. diff --git a/dash-renderer/src/AccessDenied.react.js b/dash-renderer/src/AccessDenied.react.js index 6dd7ab2808..f70625548a 100644 --- a/dash-renderer/src/AccessDenied.react.js +++ b/dash-renderer/src/AccessDenied.react.js @@ -1,10 +1,13 @@ /* global window:true, document:true */ import React from 'react'; -import {mergeRight} from 'ramda'; +import {mergeRight, once} from 'ramda'; import PropTypes from 'prop-types'; import * as styles from './styles/styles.js'; import * as constants from './constants/constants.js'; +/* eslint-disable-next-line no-console */ +const logWarningOnce = once(console.warn); + function AccessDenied(props) { const {config} = props; const fid = config.fid; @@ -28,9 +31,14 @@ function AccessDenied(props) { { - document.cookie = - `${constants.OAUTH_COOKIE_NAME}=; ` + - 'expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */ + try { + document.cookie = + `${constants.OAUTH_COOKIE_NAME}=; ` + + 'expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + } catch (e) { + logWarningOnce(e); + } window.location.reload(true); }} > diff --git a/dash-renderer/src/actions/index.js b/dash-renderer/src/actions/index.js index c6c9b6c772..fc582775a7 100644 --- a/dash-renderer/src/actions/index.js +++ b/dash-renderer/src/actions/index.js @@ -16,6 +16,7 @@ import { lensPath, mergeLeft, mergeDeepRight, + once, path, pluck, propEq, @@ -54,10 +55,18 @@ export function hydrateInitialOutputs() { }; } +/* eslint-disable-next-line no-console */ +const logWarningOnce = once(console.warn); + export function getCSRFHeader() { - return { - 'X-CSRFToken': cookie.parse(document.cookie)._csrf_token, - }; + try { + return { + 'X-CSRFToken': cookie.parse(document.cookie)._csrf_token, + }; + } catch (e) { + logWarningOnce(e); + return {}; + } } function triggerDefaultState(dispatch, getState) { diff --git a/tests/integration/renderer/test_iframe.py b/tests/integration/renderer/test_iframe.py new file mode 100644 index 0000000000..6a4a93f52c --- /dev/null +++ b/tests/integration/renderer/test_iframe.py @@ -0,0 +1,57 @@ +from multiprocessing import Value + +import dash +from dash.dependencies import Input, Output +from dash.exceptions import PreventUpdate + +import dash_html_components as html + + +def test_rdif001_sandbox_allow_scripts(dash_duo): + app = dash.Dash(__name__) + call_count = Value("i") + + N_OUTPUTS = 50 + + app.layout = html.Div([ + html.Button("click me", id="btn"), + ] + [html.Div(id="output-{}".format(i)) for i in range(N_OUTPUTS)]) + + @app.callback( + [Output("output-{}".format(i), "children") for i in range(N_OUTPUTS)], + [Input("btn", "n_clicks")] + ) + def update_output(n_clicks): + if n_clicks is None: + raise PreventUpdate + + call_count.value += 1 + return ["{}={}".format(i, i + n_clicks) for i in range(N_OUTPUTS)] + + @app.server.after_request + def apply_cors(response): + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Authorization" + return response + + dash_duo.start_server(app) + + iframe = """ + + + + + """ + + html_content = iframe.format(dash_duo.server_url) + + dash_duo.driver.get("data:text/html;charset=utf-8," + html_content) + + dash_duo.driver.switch_to.frame(0) + + dash_duo.wait_for_element('#output-0') + dash_duo.wait_for_element_by_id('btn').click() + dash_duo.wait_for_element('#output-0').text == '0=1' + + assert len(dash_duo.get_logs()) != 0