Я пишу приложение facebook на основе iframe. Теперь я хочу использовать одну и ту же страницу html для отображения обычного веб-сайта, а также страницы холста в facebook. Я хочу знать, могу ли я определить, была ли страница загружена внутри iframe или непосредственно в браузере?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Браузеры могут блокировать доступ к window.top из-за та же политика происхождения. Ошибки IE также имеют место. Вот рабочий код:
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
top и self являются объектами window (вместе с parent), поэтому вы видите, является ли ваше окно верхним окном.
Кажется, это нормально работает в Firefox. Это работает и в других браузерах?
Имея страницу с iframe внутри iframe, чтобы проверить из моего дочернего iframe, был ли мой родительский iframe встроен в страницу, я использовал if (parent === top)
@sglessard Если дочерняя страница и родительская страница из разных доменов, Firefox будет жаловаться на одну и ту же политику происхождения (www.w3.org/Security/wiki/Same_Origin_Policy), и код не будет работать
это НЕ говорит вам, что родитель обязательно facebook, хотя
У меня это работает в IE 11, 10 и 9. Также он работает в FF и Opera и, конечно же, в Chrome. У меня не было никаких проблем с этим.
Достаточно просто, спасибо :). Хотя мне это нужно для критически важной части моего проекта. Может это удержать ?? Я имею в виду, можно ли это обмануть?
@MNVOH, проверьте это в IE8
@MNVOH Обновил решение, проверьте его.
Последнее обновление вызывает у меня ошибку. Не улавливает ли параметр?
Да, я почти уверен, что catch требует параметра ... У меня тоже не работает как есть.
Это не удастся, если происхождение совпадет. Window.top будет доступен для iframe.
Я думал, что это не работает в IE11 (Windows 7), пока не Я включил консоль разработчика; тогда это сработало. Оказывается, в моем коде был «console.info», чтобы показать мне, что происходило в логике, и похоже, что IE11 выдает ошибку, если вы вызываете console.info, а консоль не подключена. Без регистрации у меня это работает в Chrome, Firefox, IE11 и Safari (Windows 7 и OSX, где это необходимо).
Для тех, кто все еще использует новейшие технологии 1995 года, window.self !== window.top возвращает true при запуске из содержимого frame, или жеiframe.
«Браузеры могут блокировать доступ к window.top из-за той же политики происхождения». Ты уверен? Он может блокировать доступ к свойствам на window.top, но я понимаю, что он не будет блокировать доступ к самому window.top. Страница, на которую вы указали ссылку, похоже, поддерживает это (см. Доступ к API-интерфейсу скриптов из разных источников).
@JoshHabdas - Я хотел бы понять, почему это лучший подход?
Одним словом @phillyslick: выразительность.
Как это замечательно и полезно!
@JoshHabdas frameElement будет давать ложные срабатывания, если страница встроена в страницу из другого источника: "возвращает ... null, если элемент либо находится на верхнем уровне, либо встроен в документ с другим источником скрипта; то есть перекрестно -начальные ситуации ". developer.mozilla.org/en-US/docs/Web/API/Window/frameElement
@Skitterm, спасибо. теперь я понимаю, почему этот подход лучше.
Роборг прав, но я хотел добавить примечание.
В IE7 / IE8, когда Microsoft добавила вкладки в свой браузер, они сломали одну вещь, которая нанесет ущерб вашему JS, если вы не будете осторожны.
Представьте себе этот макет страницы:
MainPage.html
IframedPage1.html (named "foo")
IframedPage2.html (named "bar")
IframedPage3.html (named "baz")
Теперь во фрейме «baz» вы нажимаете ссылку (нет цели, загружается во фрейме «baz»), все работает нормально.
Если загружаемая страница вызовет ее special.html, использует JS, чтобы проверить, есть ли у «нее» родительский фрейм с именем «bar», он вернет true (ожидалось).
Теперь предположим, что страница special.html при загрузке проверяет родительский фрейм (на наличие и его имя, и если это "bar", он перезагружается в рамке bar. Например
if (window.parent && window.parent.name == 'bar'){
window.parent.location = self.location;
}
Все идет нормально. Теперь идет ошибка.
Допустим, вместо того, чтобы щелкнуть исходную ссылку, как обычно, и загрузить страницу special.html во фрейме «baz», вы щелкнули по ней средней кнопкой мыши или решили открыть ее в новой вкладке.
Когда эта новая вкладка загружается (без родительских фреймов!) IE войдет в бесконечный цикл загрузки страницы!, потому что IE «копирует» структуру фрейма в JavaScript, так что новая вкладка ДЕЙСТВИТЕЛЬНО имеет родителя, а у этого родителя ИМЕЕТ имя «bar».
Хорошая новость в том, что проверка:
if (self == top){
//this returns true!
}
в этой новой вкладке возвращает true, и поэтому вы можете проверить это странное условие.
Исправлена ли ошибка тем временем? Я не могу воспроизвести это в IE8. Я всегда получаю правильный window.parent.
Не уверен - я снова запущу свой набор тестов для IE9 и ниже и опубликую обновление.
Это 2021 год, а я все еще танцую на могиле IE.
К сожалению, я все еще поддерживаю IE ... хотя я определенно буду отмечать день, когда мне больше не нужно. ;-)
Поскольку вы запрашиваете в контексте приложения facebook, вы можете рассмотреть возможность обнаружения этого на сервере при выполнении первоначального запроса. Facebook передает кучу данных строки запроса, включая ключ fb_sig_user, если он вызывается из iframe.
Поскольку вам, вероятно, все равно придется проверять и использовать эти данные в своем приложении, используйте их для определения соответствующего контекста для рендеринга.
Используйте эту функцию javascript в качестве примера того, как это сделать.
function isNoIframeOrIframeInMyHost() {
// Validation: it must be loaded as the top page, or if it is loaded in an iframe
// then it must be embedded in my own domain.
// Info: IF top.location.href is not accessible THEN it is embedded in an iframe
// and the domains are different.
var myresult = true;
try {
var tophref = top.location.href;
var tophostname = top.location.hostname.toString();
var myhref = location.href;
if (tophref === myhref) {
myresult = true;
} else if (tophostname !== "www.yourdomain.com") {
myresult = false;
}
} catch (error) {
// error is a permission error that top.location.href is not accessible
// (which means parent domain <> iframe domain)!
myresult = false;
}
return myresult;
}
Принятый ответ не сработал для меня внутри сценария содержимого расширения Firefox 6.0 (Addon-SDK 1.0): Firefox выполняет сценарий содержимого в каждом: окне верхнего уровня и во всех окнах iframe.
Внутри скрипта содержимого я получаю следующие результаты:
(window !== window.top) : false
(window.self !== window.top) : true
Странность этого вывода заключается в том, что он всегда один и тот же, независимо от того, выполняется ли код внутри iframe или в окне верхнего уровня.
С другой стороны, Google Chrome, похоже, выполняет мой сценарий содержимого только один раз в окне верхнего уровня, поэтому вышеуказанное не будет работать вообще.
Что в итоге сработало для меня в сценарии содержимого в обоих браузерах:
console.info(window.frames.length + ':' + parent.frames.length);
Без iframe он печатает 0:0, в окне верхнего уровня, содержащем один фрейм, он печатает 1:1, а в единственном iframe документа он печатает 0:1.
Это позволяет моему расширению определять в обоих браузерах, есть ли какие-либо фреймы iframe, и дополнительно в Firefox, если оно запускается внутри одного из iframe.
Попробуйте document.defaultView.self === document.defaultView.top или window !== window.top. В сценарии содержимого SDK надстройки Firefox глобальный объект self - это объект, используемый для связи с основным сценарием.
Это древний фрагмент кода, который я использовал несколько раз:
if (parent.location.href == self.location.href) {
window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
Чтобы добавить Энеко Алонсо: это работает (parent.location == self.location)
@piotr_cz, он работает, только если политика одного и того же происхождения разрешает доступ к parent.location. В противном случае выдается исключение
Я использую это:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
Зачем вы это делаете: .indexOf("HTMLIFrameElement")?
.indexOf ('foobarbaz')> -1 - это способ проверить "совпадение подстроки" (т.е. можно ли найти 'foobarbaz' где-нибудь в строке?), но без использования регулярных выражений. Логически эквивалентен .match (/ foobarbaz /). Он (раньше :-) работал с более широким спектром браузеров и все еще может использоваться либо по привычке, либо из-за опасений по поводу производительности механизма регулярных выражений.
if (window.frames.length != parent.frames.length) { page loaded in iframe }
Но только если количество фреймов отличается на вашей странице и странице, которая загружает вас в iframe. Не делайте iframe на своей странице, чтобы иметь 100% гарантию результата этого кода
Не очень элегантное решение для определения того, находится ли окно внутри iframe или нет, и нет никакой гарантии, что вы также сможете читать из parent.frames.
Напишите этот javascript на каждой странице
if (self == top)
{ window.location = "Home.aspx"; }
Затем он автоматически перенаправляется на домашнюю страницу.
Ваше перенаправление будет работать, когда вы этого не сделаете, если frame. Поэтому я не смогу перемещаться по вашему сайту. Во-вторых, существуют методы предотвращения перенаправления с родительской страницы.
Если вы хотите узнать, обращается ли пользователь к вашему приложению с вкладки страницы facebook или холста, проверьте подписанный запрос. Если вы его не получили, вероятно, пользователь не получает доступ из Facebook. Чтобы убедиться, что подтвердите структуру полей signed_request и содержимое полей.
С помощью php-sdk вы можете получить подписанный запрос следующим образом:
$signed_request = $facebook->getSignedRequest();
Вы можете узнать больше о подписанном запросе здесь:
https://developers.facebook.com/docs/reference/php/facebook-getSignedRequest/
и тут:
https://developers.facebook.com/docs/reference/login/signed-request/
Находясь в iframe из того же источника, что и родительский, метод window.frameElement возвращает элемент (например, iframe или object), в который встроено окно. В противном случае, при просмотре в контексте верхнего уровня или если родительский и дочерний фреймы имеют разное происхождение, он будет оцениваться как null.
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
Это Стандарт HTML с базовой поддержкой во всех современных браузерах.
Обратите внимание, что попытка чтения frameElement вызовет исключение SecurityError в iframe с перекрестным происхождением, согласно W3C (WHATWG говорит, что вместо этого он должен вернуть null). Так что вы можете заключить его в оператор try...catch.
получение null внутри и снаружи iframe
В описании в MDN говорится, что «если документ, в который он встроен, имеет другое происхождение (например, находился в другом домене), он равен нулю».
Просто чтобы прояснить, какой должна быть отдача: Returns the element (such as <iframe> or <object>) in which the window is embedded, or null if the element is either top-level or is embedded into a document with a different script origin; that is, in cross-origin situations.
Я на самом деле использовал для проверки window.parent, и это сработало для меня, но в последнее время окно является циклическим объектом и всегда имеет родительский ключ, iframe или без iframe.
Поскольку в комментариях предлагается жесткое сравнение с window.parent, работает. Не уверен, что это сработает, если iframe - это та же веб-страница, что и родительская.
window === window.parent;
в chrome в контексте главного окна window.parent === window верно. Значит, ваш ответ неверен. И это сравнение можно было использовать для проверки (по крайней мере, в хроме, в других браузерах не тестировал).
НЕ РАБОТАЕТ в контексте iFrame, но `if (window === window.parent) {` работает. Я не отмечаю вас, потому что не знаю, почему это не работает
Я не уверен, как этот пример работает для старых веб-браузеров, но я без проблем использую его для IE, Firefox и Chrome:
var iFrameDetection = (window === window.parent) ? false : true;
Или просто !(window===window.parent)
окно! == window.parent
Другие решения у меня не сработали. Этот работает во всех браузерах:
Один из способов защиты от кликджекинга - это включить на каждую страницу скрипт "взлома фреймов", который не должен быть во фрейме. Следующая методика предотвратит создание веб-страницы даже в устаревших браузерах, которые не поддерживают X-Frame-Options-Header.
В элементе HEAD документа добавьте следующее:
<style id = "antiClickjack">body{display:none !important;}</style>
Сначала примените идентификатор к самому элементу стиля:
<script type = "text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
Таким образом, все может быть в документе HEAD, и вам понадобится только один метод / taglib в вашем API.
Ссылка: https://www.codemagi.com/blog/post/194
кадрирование - не единственная угроза, как насчет возражений (имеется в виду использование тега объекта HTML5, который заменяет тег iframe) .. я думаю, что это не сработает против этого ..
Это оказалось для меня самым простым решением.
<p id = "demofsdfsdfs"></p>
<script>
if (window.self !== window.top) {
//run this code if in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "in frame";
}else{
//run code if not in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "no frame";
}
</script>
if ( window.location !== window.parent.location )
{
// The page is in an iframe
}
else
{
// The page is not in an iframe
}
кроме случаев, когда вы открываете iframe своего собственного веб-сайта.
function amiLoadedInIFrame() {
try {
// Introduce a new propery in window.top
window.top.dummyAttribute = true;
// If window.dummyAttribute is there.. then window and window.top are same intances
return !window.dummyAttribute;
} catch(e) {
// Exception will be raised when the top is in different domain
return true;
}
}
Следуя тому, что сказал @magnoz, вот кодовая реализация его ответа.
constructor() {
let windowLen = window.frames.length;
let parentLen = parent.frames.length;
if (windowLen == 0 && parentLen >= 1) {
this.isInIframe = true
console.info('Is in Iframe!')
} else {
console.info('Is in main window!')
}
}
Несколько хороших способов (включая комментарии): tommcfarlin.com/check-if-a-page-is-in-an-iframe