Migrating legacy web applications into modern architectures often involves embedding older interfaces within frames or iframes. A common architectural pattern uses a left-sided navigation panel to control the content displayed in a right-sided frame. How ever, when the navigation panel and the content frame belong to different origins, attempting to programmatically update the content frame's source triggers a browser security restriction.
The Same-Origin Policy strictly prevents a script from one origin from accessing or modifying the DOM properties of a window or frame residing in a different origin. If a sidebar frame attempts to execute window.parent.frames["contentFrame"].src = "https://external-site.com", the browser will block the action and throw the following exception:
Uncaught DOMException: Blocked a frame with origin "https://local-domain.com" from accessing a cross-origin frame.
While this policy is crucial for web security—preventing unauthorized script injection into third-party pages—it poses a logistical challenge for legitimate integration scenarios. Directly manipulating the src attribute of an existing cross-origin frame is forbidden, but the browser inherently allows creating a new frame element and assigning a URL to it. By leveraging this distinction, we can bypass the restriction by destroying the current frame element and constructing a replacement frame with the desired URL.
Implementation Strategy
To acheive this, the parent layout document must expose a global function responsible for swapping the frame. The navigation sidebar will invoke this parent function, passing the target URL. The parent function then removes the existing content frame from the DOM, dynamically creates a new frame element, sets its src, and appends it back to the layout structure.
Parent Layout (layout.html)
<html>
<head>
<script src="jquery-3.6.0.min.js"></script>
<script type="text/javascript">
function swapContentPane(targetUrl) {
// Remove the existing content frame
const currentPane = document.getElementById('contentPane');
if (currentPane) {
currentPane.remove();
}
// Construct a fresh frame element
const freshPane = document.createElement('frame');
freshPane.id = 'contentPane';
freshPane.name = 'contentPane';
freshPane.src = targetUrl;
// Append the new frame to the frameset layout
const appLayout = document.getElementById('appLayout');
appLayout.appendChild(freshPane);
}
</script>
</head>
<frameset cols="30%, 70%" id="appLayout" name="appLayout">
<frame id="navPane" name="navPane" src="sidebar.html" />
<frame id="contentPane" name="contentPane" src="https://initial-site.com" />
</frameset>
</html>
Navigation Sidebar (sidebar.html)
<html>
<head>
<script src="jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#navigateBtn').click(function() {
// Trigger the parent's frame swapping mechanism
window.top.swapContentPane("https://new-destination.com");
});
});
</script>
</head>
<body>
<button id="navigateBtn">Load New Content</button>
</body>
</html>
When dealing with distributed systems and cross-domain integrations, frame source manipulation is just one obstacle. Developers must also account for session state and cookie sharing across different subdomains or entirely separate domains, which requires additional server-side configurations and CORS policies.