| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta name="viewport" content="user-scalable=no"> |
| <title>Tests layout of absolutely positioned modal dialogs.</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <style> |
| /* Remove body margin and dialog styles for easier positioning expected values */ |
| body { |
| height: 10000px; |
| margin: 0; |
| } |
| |
| dialog { |
| border: 0; |
| padding: 0; |
| height: auto; |
| width: auto; |
| } |
| |
| #absolute-div { |
| position: absolute; |
| top: 800px; |
| height: 50px; |
| width: 90%; |
| } |
| |
| #relative-div { |
| position: relative; |
| top: 20px; |
| height: 30px; |
| } |
| </style> |
| </head> |
| <dialog >It is my dialog.</dialog> |
| <div id="absolute-div"> |
| <div id="relative-div"></div> |
| </div> |
| <script> |
| "use strict"; |
| |
| function checkNotVerticallyCentered(dialog) { |
| var centeredTop = (window.innerHeight - dialog.offsetHeight) / 2; |
| assert_not_equals(dialog.getBoundingClientRect().top, centeredTop); |
| } |
| |
| function checkVerticallyCentered(dialog) { |
| var centeredTop = (window.innerHeight - dialog.offsetHeight) / 2; |
| // Using approx equals because getBoundingClientRect() and centeredTop |
| // are calculated by different parts of the engine. Due to the loss |
| // of precision, the numbers might not equal exactly. |
| assert_approx_equals(dialog.getBoundingClientRect().top, centeredTop, 1); |
| } |
| |
| function reset() { |
| document.body.style.width = "auto"; |
| if (dialog.open) |
| dialog.close(); |
| dialog.remove(); |
| document.body.appendChild(dialog); |
| window.scroll(0, 500); |
| } |
| |
| var dialog = document.querySelector('dialog'); |
| var absoluteContainer = document.querySelector('#absolute-div'); |
| var relativeContainer = document.querySelector('#relative-div'); |
| reset(); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| }, "showModal() should center in the viewport"); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| assert_equals(window.getComputedStyle(dialog).top, 'auto'); |
| assert_equals(window.getComputedStyle(dialog).bottom, 'auto'); |
| |
| dialog.style.height = '20px'; |
| dialog.showModal(); |
| |
| assert_not_equals(window.getComputedStyle(dialog).top, 'auto'); |
| assert_not_equals(window.getComputedStyle(dialog).bottom, 'auto'); |
| |
| // Set back original value to 'height'. |
| dialog.style.height = 'auto'; |
| }, "The dialog is a positioned element, so the top and bottom should not have style auto."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| dialog.close(); |
| window.scroll(0, 2 * window.scrollY); |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| }, "Dialog should be recentered if showModal() is called after close()"); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| var expectedTop = dialog.getBoundingClientRect().top; |
| window.scroll(0, window.scrollY * 2); |
| |
| // Trigger relayout |
| document.body.offsetHeight; |
| |
| window.scroll(0, window.scrollY / 2); |
| assert_equals(dialog.getBoundingClientRect().top, expectedTop); |
| }, "Dialog should not recenter on relayout."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.style.height = '20000px'; |
| dialog.showModal(); |
| assert_equals(dialog.getBoundingClientRect().top, 0); |
| |
| // Set back original value to 'height'. |
| dialog.style.height = 'auto'; |
| }, "A tall dialog should be positioned at the top of the viewport."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| document.body.style.width = '4000px'; |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| |
| }, "The dialog should be centered regardless of the presence of a horizontal scrollbar."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.remove(); |
| absoluteContainer.appendChild(dialog); |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| dialog.close(); |
| |
| dialog.remove(); |
| relativeContainer.appendChild(dialog); |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| }, "Centering should work when dialog is inside positioned containers."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| var expectedTop = dialog.getBoundingClientRect().top; |
| relativeContainer.style.display = 'none'; |
| relativeContainer.style.display = 'block'; |
| assert_equals(dialog.getBoundingClientRect().top, expectedTop); |
| }, "A centered dialog's position should survive becoming display:none temporarily."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| // Remove and reinsert so that the document position isn't changed by the second remove and reinsert |
| dialog.remove(); |
| relativeContainer.appendChild(dialog); |
| |
| dialog.showModal(); |
| assert_not_equals(dialog.getBoundingClientRect().top, relativeContainer.getBoundingClientRect().top); |
| dialog.remove(); |
| |
| relativeContainer.appendChild(dialog); |
| assert_equals(dialog.parentNode, relativeContainer); |
| assert_equals(dialog.getBoundingClientRect().top, relativeContainer.getBoundingClientRect().top); |
| }, "Dialog should lose centering when removed from the document."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| dialog.style.top = '0px'; |
| var expectedTop = dialog.getBoundingClientRect().top; |
| dialog.close(); |
| dialog.showModal(); |
| assert_equals(dialog.getBoundingClientRect().top, expectedTop); |
| |
| // Set back original value to 'top'. |
| dialog.style.top = 'auto'; |
| }, "Dialog's specified position should survive after close() and showModal()."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.showModal(); |
| dialog.removeAttribute('open'); |
| window.scroll(0, window.scrollY * 2); |
| checkNotVerticallyCentered(dialog); |
| dialog.showModal(); |
| checkVerticallyCentered(dialog); |
| }, "Dialog should be recentered if showModal() is called after removing 'open'."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| dialog.remove(); |
| absoluteContainer.appendChild(dialog); |
| absoluteContainer.style.display = 'none'; |
| dialog.showModal(); |
| absoluteContainer.style.display = 'block'; |
| // Since dialog's containing block is the ICB, it's statically positioned after <body>. |
| assert_equals(dialog.getBoundingClientRect().top, document.body.getBoundingClientRect().bottom); |
| }, "Dialog should not be centered if showModal() was called when an ancestor had display 'none'."); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| var offset = 50; |
| dialog.style.top = offset + 'px'; |
| dialog.showModal(); |
| assert_equals(dialog.getBoundingClientRect().top + window.scrollY, offset); |
| // Set back original value to 'top'. |
| dialog.style.top = 'auto'; |
| }, "A dialog with specified 'top' should be positioned as usual"); |
| |
| test(function() { |
| this.add_cleanup(reset); |
| |
| var offset = 50; |
| dialog.style.bottom = offset + 'px'; |
| dialog.showModal(); |
| assert_equals(dialog.getBoundingClientRect().bottom + window.scrollY, window.innerHeight - offset); |
| // Set back original value to 'bottom'. |
| dialog.style.bottom = 'auto'; |
| }, "A dialog with specified 'bottom' should be positioned as usual"); |
| </script> |