CSS/JavaScript/взлома: обнаружение: посещение стиля по ссылке * без * проверки непосредственно или сделать это быстрее меня

Это для исследовательских целей http://cssfingerprint.com

Рассмотрим следующий код:

<style>
  div.csshistory a { display: none; color: #00ff00;}
  div.csshistory a:visited { display: inline; color: #ff0000;}
</style>

<div id="batch" class="csshistory">
  <a id="1" href="#" onclick="location.href='http://foo.com'; return false;">anything you want here</a>
  <a id="2" href="#" onclick="location.href='http://bar.com'; return false;">anything you want here</a>
  [etc * ~2000]
</div>

Моя цель - определить, была ли рендерирована foo с помощью: посещенного стиля.

  • Я хочу определить, посетил ли foo.com без прямого просмотра $('1').getComputedStyle (или в Internet Explorer, currentStyle) или любого другого прямого метода для этого элемента.

    Цель этого заключается в том, чтобы обойти потенциальное ограничение браузера, которое предотвратило бы прямой контроль стиля посещенных ссылок.

    Например, возможно, вы можете поместить подэлемент в тег <a> или напрямую проверить стиль текста; и т.д. Любой метод, который прямо или нецелесообразно полагаться на $('1').anything, является приемлемым. Возможно, необходимо сделать что-то умное с ребенком или родителем.

    Обратите внимание, что только для целей этого сценария сценарий заключается в том, что браузер будет ссылаться на JavaScript обо всех свойствах элемента <a> (но не на других) и что он будет отображать только color: в :visited. Поэтому способы, которые полагаются, например, размер текста или background-image не будет соответствовать этому требованию.

  • Я хочу улучшить скорость моих текущих методов скремблирования.

    Большинство времени (по крайней мере, с помощью метода jQuery в Firefox) расходуется на document.body.appendChild(batch), поэтому найти способ улучшить этот вызов, вероятно, будет наиболее эффективным.

    См. http://cssfingerprint.com/about и http://cssfingerprint.com/results для текущих результатов теста скорости.

Используемые мной методы можно увидеть на http://github.com/saizai/cssfingerprint/blob/master/public/javascripts/history_scrape.js

Подводя итоги для tl; dr, они:

  • установите цвет или дисплей на: посещенный на каждый из них и проверьте каждый из них напрямую w/getComputedStyle
  • поместите идентификатор ссылки (плюс пробел) внутри тега <a> и с помощью jQuery: visible selector извлеките только видимый текст (= просмотренные идентификаторы ссылок)

FWIW, я белая шляпа, и я делаю это в консультации с EFF и некоторые другие довольно известные исследователи безопасности.

Если вы внесете новый метод или ускорителю, вы получите благодарность http://cssfingerprint.com/about (если вы хотите быть:-P ) и, возможно, в будущей опубликованной статье.

ETA: награда будет вознаграждена только за предложения, которые

  • может в Firefox избегать гипотетического ограничения, описанного в пункте 1 выше, или
  • выполняйте как минимум на 10% быстрее, в любом браузере, для которого у меня достаточно текущих данных, чем мои наилучшие методы, перечисленные на графике http://cssfingerprint.com/about

В случае, если более чем одно предложение соответствует любому критерию, выигрывает тот, который лучше всего выигрывает.

ETA 2: Я добавил варианты ширины двух предыдущих лучших методов тестирования (reuse_noinsert, лучше всего на Firefox/Mozilla и mass_insert, его очень близком конкуренте). Несколько раз просмотрите http://cssfingerprint.com из разных браузеров; Я автоматически получу результаты теста скорости, поэтому мы узнаем, насколько это лучше, чем предыдущие методы, и если да, то насколько. Спасибо!

ETA 3: Текущие тесты показывают экономию скорости, используя offsetWidth (а не getCalculatedStyle/currentStyle) ~ 2 мс (1,8%) в Chrome и ~ 24 мс (4.3%) в Firefox, что не 10% я хотел получить солидную награду. Есть идея, как выложить остальную часть этого 10%?

Ответ 1

[новое обновление]

Если вы хотите получить результаты только для визуального представления, самым быстрым методом будет использование счетчика CSS.

CSS

body{
    counter-reset: visited_counter;
}

a:visited{
    counter-increment: visited_counter;
}

#results:before{
    content:counter(visited_counter);
}

Это добавит количество посещенных ссылок перед элементом с идентификаторами 'results'.

К сожалению, нет способа получить к нему доступ из JavaScript, вы можете отображать его только.


[начальный ответ]

Вы знаете, что jQuery поддерживает селектор :visited прямо?

Как $('a:visited')

[обновление]

В качестве альтернативы вы можете применить свойство CSS, которое не полагается на getComputedStyle для извлечения..

Как a:visited{height:1px;display:block;}, а затем проверьте offsetHeight.

Ответ 2

  • добавьте дочерний элемент внутри якоря (например, span)
  • использовать color : inherit
  • определить цвет дочернего элемента (JS)

caveat: afaik он не будет работать на lte ie7

для lte ie7 ull имеем to

  • добавить visibility : hidden в a:visited и visibility : inherit для дочернего
  • проверьте видимость дочернего элемента, используя javascript (hidden = visited)

Ответ 3

Аналогичная идея, но обойти стороной .getComputedStyle():

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">

        <style type="text/css">
            a:visited { display: inline-block; font-family: monospace; }
            body { font-family: sans-serif; }
        </style>

        <script type="text/javascript">
            function test() {
                var visited = document.getElementById("v").childNodes[1].firstChild.clientWidth;
                var unvisited = document.getElementById("u").childNodes[1].firstChild.clientWidth;
                var rows = document.getElementsByTagName("tr");

                for (var i = 1, length = rows.length; i < length; i++) {
                    var row = rows[i];
                    var link = row.childNodes[1].firstChild;
                    var width = link.clientWidth;

                    row.firstChild.appendChild(document.createTextNode(link.href));
                    row.childNodes[2].appendChild(document.createTextNode(width === visited ? "yes" : (width === unvisited ? "no" : "unknown")));
                }
            }
        </script>
    </head>

    <body onload="test()">
        <table>
            <tr><th>url</th><th>link</th><th>visited?</th></tr>
            <tr id="u"><td></td><td><a href="http://invalid_host..mplx/">l</a></td><td></td>
            <tr id="v"><td></td><td><a href="css-snoop.html">l</a></td><td></td>
            <tr><td></td><td><a href="http://stackoverflow.com/">l</a></td><td></td>
            <tr><td></td><td><a href="http://www.dell.com/">l</a></td><td></td>
        </table>
    </body>
</html>

Трюк, конечно же, заключается в том, что посещаемые и невидимые ссылки имеют разную ширину (здесь, используя sans-serf vs. monospace fonts) и устанавливая их на inline-block, чтобы их ширину можно было получить через clientWidth, Протестировано для работы с FF3.6, IE7, Chrome 4 и Opera 10.

В моих тестах доступ к clientWidth был последовательно быстрее, чем все, что основывалось на вычисленных стилях (иногда на ~ 40%, но широко варьировалось).

(О, и извинения за глупость <body onload="...">, это было слишком долго, поскольку я пытался делать события в IE без рамки, и мне надоело бороться с ним.)

Ответ 4

Поскольку все версии IE (да, даже версия 8, если вы включили quirks) поддерживают выражения CSS, свойство цвета по-прежнему небезопасно. Вероятно, вы можете ускорить тестирование IE с помощью этого (непроверенного):

a:visited { color: expression( arrVisited.push(this.href) ); }

Кроме того, на ваш вопрос это не распространяется, но вы можете, конечно, установить свойства в дочерних узлах очень легко, чтобы инициировать обнаружение, и любое решение также должно было бы предотвратить это:

a.google:visited span { background-image: url(http://example.com/visited/google); }

Вам необходимо защитить соседних братьев и сестер, а не только потомков:

a.google:visited + span { }

Также непроверенный, но вы, вероятно, можете сделать тяжелое ускорение, используя свойство content, чтобы изменить DOM, а затем некоторый XPath, чтобы найти новые узлы.

a.google:visited:before {content: "visited"; visibility: hidden;}

XPath:

visited links = document.evaluate('//a[text()="visited"]')