竹磬网-邵珠庆の日记 生命只有一次,你可以用它来做些更多伟大的事情–Make the world a little better and easier


36月/130

让你的IE6、7、8 都很好的支持HTML5标签

发布在 邵珠庆

 

在旧有的浏览器里面,很多元素都是不支持的,即使解析出来也是内联标签。所以某位外国大牛就写了JS把文本中的一些标记替换成了块标签,从而解决了IE的很多历史遗留问题。这对于HTML5的使用推广来说是一个历史性的变革,也能很好的解决一些兼容性问题。

https://github.com/sebastianbergmann/php-code-coverage/pull/128

JS代码:

001 /**
002 * @preserve HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
003 */
004 ;(function(window, document) {
005 /*jshint evil:true */
006   /** version */
007   var version = '3.6.2pre';
008  
009   /** Preset options */
010   var options = window.html5 || {};
011  
012   /** Used to skip problem elements */
013   var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
014  
015   /** Not all elements can be cloned in IE **/
016   var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
017  
018   /** Detect whether the browser supports default html5 styles */
019   var supportsHtml5Styles;
020  
021   /** Name of the expando, to work with multiple documents or to re-shiv one document */
022   var expando = '_html5shiv';
023  
024   /** The id for the the documents expando */
025   var expanID = 0;
026  
027   /** Cached data for each document */
028   var expandoData = {};
029  
030   /** Detect whether the browser supports unknown elements */
031   var supportsUnknownElements;
032  
033   (function() {
034     try {
035         var a = document.createElement('a');
036         a.innerHTML = '<xyz></xyz>';
037         //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
038         supportsHtml5Styles = ('hidden' in a);
039  
040         supportsUnknownElements = a.childNodes.length == 1 || (function() {
041           // assign a false positive if unable to shiv
042           (document.createElement)('a');
043           var frag = document.createDocumentFragment();
044           return (
045             typeof frag.cloneNode == 'undefined' ||
046             typeof frag.createDocumentFragment == 'undefined' ||
047             typeof frag.createElement == 'undefined'
048           );
049         }());
050     catch(e) {
051       supportsHtml5Styles = true;
052       supportsUnknownElements = true;
053     }
054  
055   }());
056  
057   /*--------------------------------------------------------------------------*/
058  
059   /**
060 * Creates a style sheet with the given CSS text and adds it to the document.
061 * @private
062 * @param {Document} ownerDocument The document.
063 * @param {String} cssText The CSS text.
064 * @returns {StyleSheet} The style element.
065 */
066   function addStyleSheet(ownerDocument, cssText) {
067     var p = ownerDocument.createElement('p'),
068         parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
069  
070     p.innerHTML = 'x<style>' + cssText + '</style>';
071     return parent.insertBefore(p.lastChild, parent.firstChild);
072   }
073  
074   /**
075 * Returns the value of `html5.elements` as an array.
076 * @private
077 * @returns {Array} An array of shived element node names.
078 */
079   function getElements() {
080     var elements = html5.elements;
081     return typeof elements == 'string' ? elements.split(' ') : elements;
082   }
083    
084     /**
085 * Returns the data associated to the given document
086 * @private
087 * @param {Document} ownerDocument The document.
088 * @returns {Object} An object of data.
089 */
090   function getExpandoData(ownerDocument) {
091     var data = expandoData[ownerDocument[expando]];
092     if (!data) {
093         data = {};
094         expanID++;
095         ownerDocument[expando] = expanID;
096         expandoData[expanID] = data;
097     }
098     return data;
099   }
100  
101   /**
102 * returns a shived element for the given nodeName and document
103 * @memberOf html5
104 * @param {String} nodeName name of the element
105 * @param {Document} ownerDocument The context document.
106 * @returns {Object} The shived element.
107 */
108   function createElement(nodeName, ownerDocument, data){
109     if (!ownerDocument) {
110         ownerDocument = document;
111     }
112     if(supportsUnknownElements){
113         return ownerDocument.createElement(nodeName);
114     }
115     if (!data) {
116         data = getExpandoData(ownerDocument);
117     }
118     var node;
119  
120     if (data.cache[nodeName]) {
121         node = data.cache[nodeName].cloneNode();
122     else if (saveClones.test(nodeName)) {
123         node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
124     else {
125         node = data.createElem(nodeName);
126     }
127  
128     // Avoid adding some elements to fragments in IE < 9 because
129     // * Attributes like `name` or `type` cannot be set/changed once an element
130     // is inserted into a document/fragment
131     // * Link elements with `src` attributes that are inaccessible, as with
132     // a 403 response, will cause the tab/window to crash
133     // * Script elements appended to fragments will execute when their `src`
134     // or `text` property is set
135     return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
136   }
137  
138   /**
139 * returns a shived DocumentFragment for the given document
140 * @memberOf html5
141 * @param {Document} ownerDocument The context document.
142 * @returns {Object} The shived DocumentFragment.
143 */
144   function createDocumentFragment(ownerDocument, data){
145     if (!ownerDocument) {
146         ownerDocument = document;
147     }
148     if(supportsUnknownElements){
149         return ownerDocument.createDocumentFragment();
150     }
151     data = data || getExpandoData(ownerDocument);
152     var clone = data.frag.cloneNode(),
153         i = 0,
154         elems = getElements(),
155         l = elems.length;
156     for(;i<l;i++){
157         clone.createElement(elems[i]);
158     }
159     return clone;
160   }
161  
162   /**
163 * Shivs the `createElement` and `createDocumentFragment` methods of the document.
164 * @private
165 * @param {Document|DocumentFragment} ownerDocument The document.
166 * @param {Object} data of the document.
167 */
168   function shivMethods(ownerDocument, data) {
169     if (!data.cache) {
170         data.cache = {};
171         data.createElem = ownerDocument.createElement;
172         data.createFrag = ownerDocument.createDocumentFragment;
173         data.frag = data.createFrag();
174     }
175  
176  
177     ownerDocument.createElement = function(nodeName) {
178       //abort shiv
179       if (!html5.shivMethods) {
180           return data.createElem(nodeName);
181       }
182       return createElement(nodeName, ownerDocument, data);
183     };
184  
185     ownerDocument.createDocumentFragment = Function('h,f','return function(){' +
186       'var n=f.cloneNode(),c=n.createElement;' +
187       'h.shivMethods&&(' +
188         // unroll the `createElement` calls
189         getElements().join().replace(/\w+/g, function(nodeName) {
190           data.createElem(nodeName);
191           data.frag.createElement(nodeName);
192           return 'c("' + nodeName + '")';
193         }) +
194       ');return n}'
195     )(html5, data.frag);
196   }
197  
198   /*--------------------------------------------------------------------------*/
199  
200   /**
201 * Shivs the given document.
202 * @memberOf html5
203 * @param {Document} ownerDocument The document to shiv.
204 * @returns {Document} The shived document.
205 */
206   function shivDocument(ownerDocument) {
207     if (!ownerDocument) {
208         ownerDocument = document;
209     }
210     var data = getExpandoData(ownerDocument);
211  
212     if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
213       data.hasCSS = !!addStyleSheet(ownerDocument,
214         // corrects block display not defined in IE6/7/8/9
215         'article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}'+
216         // adds styling not present in IE6/7/8/9
217         'mark{background:#FF0;color:#000}'
218       );
219     }
220     if (!supportsUnknownElements) {
221       shivMethods(ownerDocument, data);
222     }
223     return ownerDocument;
224   }
225  
226   /*--------------------------------------------------------------------------*/
227  
228   /**
229 * The `html5` object is exposed so that more elements can be shived and
230 * existing shiving can be detected on iframes.
231 * @type Object
232 * @example
233 *
234 * // options can be changed before the script is included
235 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
236 */
237   var html5 = {
238  
239     /**
240 * An array or space separated string of node names of the elements to shiv.
241 * @memberOf html5
242 * @type Array|String
243 */
244     'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
245  
246     /**
247 * current version of html5shiv
248 */
249     'version': version,
250  
251     /**
252 * A flag to indicate that the HTML5 style sheet should be inserted.
253 * @memberOf html5
254 * @type Boolean
255 */
256     'shivCSS': (options.shivCSS !== false),
257  
258     /**
259 * Is equal to true if a browser supports creating unknown/HTML5 elements
260 * @memberOf html5
261 * @type boolean
262 */
263     'supportsUnknownElements': supportsUnknownElements,
264  
265     /**
266 * A flag to indicate that the document's `createElement` and `createDocumentFragment`
267 * methods should be overwritten.
268 * @memberOf html5
269 * @type Boolean
270 */
271     'shivMethods': (options.shivMethods !== false),
272  
273     /**
274 * A string to describe the type of `html5` object ("default" or "default print").
275 * @memberOf html5
276 * @type String
277 */
278     'type''default',
279  
280     // shivs the document according to the specified `html5` object options
281     'shivDocument': shivDocument,
282  
283     //creates a shived element
284     createElement: createElement,
285  
286     //creates a shived documentFragment
287     createDocumentFragment: createDocumentFragment
288   };
289  
290   /*--------------------------------------------------------------------------*/
291  
292   // expose html5
293   window.html5 = html5;
294  
295   // shiv the document
296   shivDocument(document);
297  
298 }(this, document));

然后把html5shiv.js在head里面引入:

01 <!DOCTYPE HTML>
02 <html lang="en-US">
03     <head>
04         <meta charset="UTF-8">
05         <title></title>
06         <script type="text/javascript" src="html5.js"></script>
07         <style type="text/css">
08             nav {
09                 width:200px;
10                 height:100px;
11                 background:#f12;
12             }
13         </style>
14     </head>
15     <body>
16         <nav>Springload</nav>Springload
17     </body>
18 </html>

在IE8中打开查看你就会发现nav这个HTML5元素标签,可以正常使用和显示。

171月/1223

p3p 简洁策略及浏览器支持情况

发布在 邵珠庆

简述部分摘自某本关于P3P隐私策略的书籍.
而部分详细的表格来自w3.org.
而相关测试数据出自本人测试.如有遗漏或错误,欢迎指正.
相关资源:
1. http://www.w3.org/P3P/
2. http://www.w3.org/TR/2002/REC-P3P-20020416/


简述:
从本质上来说,P3P 策略是由一系列多选项问题的答案组成的,因此,它并不总像一个人类可读的隐私策略那样包含许多信息细节(例如,用英语或者其他某种口语语言写成的策略是用来让人阅读的,而不是让计算机识别的)。P3P策略的标准格式使它便于自动处理。同 样 ,P3P规范也包含有用于请求和传输P3P策略的协议.P3P协议所基于的HTTP协议与 Web 浏览器用来与 Web服务器进行通信的 HTTP 协议相同。 如图 1-1 所示,P3P 的用户代理使用标准的 HTTP 请求从 Web 站点上一个众所周知的地方获取 P3P 策略引用文件,并发送给发出请求的用户。这个策略引用文件指出了Web站点上各个部分所应用的P3P策略文件的位置。整个站点有可能只应用了一种策略,也 可能是网站的不同部分分别应用了几种策略。这样用户代理就可以根据用户的选择来获取合适的策略,将其解析出来并采取相应的动作
P3P 也允许站点在其他位置放置策略引用文件。在这些情况下,站点在声明策略引用文件的位置时,必须使用一个特定的 HTTP 报头,或者在应用了 P3P 策略的H T M L 文 件 内 嵌 入 一 个 L I N K 标 记 .
不论在何时设置cookie都可以用特定的HTTP报头来传送一个可选的 P3P 简洁策略.简洁策略是完整 P3P 策略的一个短小摘要,仅描述了与cooki e 相关的数据处理方式,并且不需要P3P 策略的完整的表达性能.

如何使Web站点支持P3P:
从技术角度看,使 Web站点支持 P3P 是一个很容易的过程。但是,它要求网络运营商在审视数据处理方式时比以前更加仔细,并要求他们协调域内各个主机上的策略和处理方式。以下是如何使站点支持P3P 技术的具体步骤。
1. 创建一个隐私策略。
2. 分析 cookie 的使用情况以及您的站点上的第三方内容。
3. 确定要对整个站点应用一个P3P策略还是对站点的不同部分应用不同的P3P 策略。
4. 为站点创建一个或几个 P3P 策略。
5. 为站点创建一个策略引用文件。
6. 为 P3P 配置服务器。
7. 测试站点,以确定它确实支持 P3P。
大多数支持P3P 的Web站点会在每台服务器上放置一个P3P策略引用文件,同时它还会在中央服务器上放置一个或多个P3P策略。这些站点也会将自己的服务器配置为用户在设置 cooki e 时发送 P3P 的简洁策略。P3P 策略包括以下信息:
● 如何与拥有该站点的公司、组织或个人进行联系的信息。
● 用户是否可以查找该站点的数据库中保存了自己的哪些个人信息。
● 如何解决与站点之间有关隐私的纠纷(如客户服务台、隐私封印以及与隐私相关的法律等)。
● 所收集数据的种类。
● 所收集数据的使用方式,以及用户是否能选择接受或拒绝这些用途。
● 信息是否会被共享以及何时被共享,用户是否有选择的权力。
● 对所收集的用户信息进行定期清除的策略。
有很多软件工具可以帮助Web站点开发人员开发支持P3P的站点。
要想了解最新的P3P 工具列表,请访问http://p3ptoolbox.org/tools/     和     http://www.w3.org/P3P/implementations/

简洁策略对应的 HTTP Response Header :

相关资源:http://www.w3.org/2002/04/P3Pv1-header.html 

Compact Policies(简洁策略)
简洁策略,本质上就是P3P策略的一个摘要. 他们的作用是,使用户代理,可以快速敏捷的获取到站点的P3P策略信息,所以是对性能有益的.
为了深入的解释简洁策略,按照 P3P1.0[4]规范,我们列出下面这些限制性的语法:

compact-policy-field        =   `CP="` compact-policy `"`
compact-policy                = compact-token *(" " compact-token)
compact-token                = compact-access           |

                                        compact-disputes         |
                                        compact-remedies         |
                                        compact-non-identifiable |
                                        compact-purpose          |
                                        compact-recipient        |
                                        compact-retention        |
                                        compact-categories       |
                                        compact-test 

 

compact-access           = "NOI" | "ALL" | "CAO" | "IDC" | "OTI" | "NON"

compact-disputes            = "DSP"

compact-remedies          = "COR" | "MON" | "LAW"

compact-non-identifiable = "NID"

compact-purpose           = "CUR"        | "ADM" [creq] | "DEV" [creq] | "TAI" [creq] |
                                       "PSA" [creq] | "PSD" [creq] | "IVA" [creq] | "IVD" [creq] | 

                                       "CON" [creq] | "HIS" [creq] | "TEL" [creq] | "OTP" [creq]
creq                              = "a" | "i" | "o"
compact-recipient       = "OUR" | "DEL" [creq] | "SAM" [creq] | "UNR" [creq] |
                                        "PUB" [creq] | "OTR" [creq]
compact-retention          = "NOR" | "STP" | "LEG" | "BUS" | "IND"

compact-category           = "PHY" | "ONL" | "UNI" | "PUR" | "FIN" | "COM" |

                              "NAV" | "INT" | "DEM" | "CNT" | "STA" | "POL" | 
                                        "HEA" | "PRE" | "LOC" | "GOV" | "OTC"
compact-test                  = "TST"

我们常用的简洁策略的 P3P头为 -   P3P : CP=CAO PSA OUR (其实, CP=. 就可以了.或者其他任何值都是可以的)分别对应了 :

  compact-access(访问)    :  CAO -  contact-and-other 

Identified Contact Information and Other Identified Data: access is given to identified online and physical contact information as well as to certain other identified data.
直译 : 被识别的联系信息,和其他被识别的数据: 网上,或现实中的联系信息,和某些被识别的数据,允许被访问. 
我的理解: 应该是, 允许被确认的信息和数据的访问. (允许第三方cookie的读写)

  compact-purpose(目的)  :  PSA -  pseudo-analysis .这个就不放解释了,字面意思很明显, 目的就是做身份验证、分析

  compact-recipient(受体) :  OUR - ours

Ourselves and/or entities acting as our agents or entities for whom we are acting as an agent: An agent in this instance is defined as a third party that processes data only on behalf of the service provider for the completion of the stated purposes. (e.g., the service provider and its printing bureau which prints address labels and does nothing further with the information
直译 :  我们自己,以及(或)实体作为我们自己的代理,或被我们所代理方的实体:这种情况下的代理,被定义为,相关进程数据,代表服务提供者,用来完成其所设定 服务的,第三方.(就好像,一个印刷局作为提供打印服务的,服务提供者,其只负责打印标签神马的,但是却不会进一步,对相关的信息,做任何事情 )
我的理解:声明使用相关信息的人是谁.这里声明是第三方自己, 或作为代理.需要操作第三方Cookie. 大概就是这个意思.

  ps : 其他项就不列举,基于浏览器中只有IE支持.(chrome 部分支持).这一事实.深入研究没有必要. 如果你有兴趣,可以去相关链接查看文档.



用户代理对简洁策略支持的状况 和实现, 拿IE6来说:

IE6 ,可以自动核对设置了 cooki e的站点的 P3P 简洁策略。用户也可以配置 IE6 来过滤那些没有简洁策略的cookie,或那些具有与他们的偏好不相匹配的简洁策略。当 cookie 被阻止时,IE6还会在浏览器的右下角显示一个“眼睛”符号。用户也可以从Vi ew(查看)菜单中选择Privacy Report(隐私报告)命令来让 IE6 获取站点的 P3P 策略,并生成及显示一个人们可读取的版本

对于众多提示性的反馈,是十分符合 P3P隐私策略的.即用户代理应该在恰当的时候,提醒用户.


已测知的问题:

IE6 第三方cookie 在有p3p头(使用p3p简介策略时) ,JS虽然有读写权限,但是在写的时候有个bug. 即,对于某第三方页面,如果是首次读到.其P3P头的话,则 js的写权限是没有的.必须要第二次访问到这个页面,且这个页面存在第三方cookie操作时,才允许JS写入Cookie.当然,读是一直没问题的.

Safair3,则顽固到连Post方式都无法写入第三方Cookie.

Safari4+ 系列则有自己一套隐私策略,而完全无视P3P的存在:
在其不支持P3P的情况下,其策略为. 默认设置浏览器禁止第三方Cookie操作.那么此时,无论JS 还是 HTTP ,都无写入Cookie的权限,而仅具备读的权限. 
除非, 进行表单Post时,才允许第三方Cookie的写入.参考下面的代码: (在//www.a.com/test.htm 中的代码)

if(Safari4或Safari5){
    var ifm = document.createElement('iframe');
    ifm.name ="postforcookie";
    ifm.src="about:blank";
    document.body.appendChild(ifm);
   
    var form = document.createElement('form');
    form.target = 'postforcookie';
    form.action = '//www.php.com/test.php';
    document.body.appendChild(form);
   
    Cookie.setCookie = function () {
        form.submit();
    }
}  

假设,setCookie是一个写入第三方Cookie的函数,则在Sacari4,Sacari5下劫持他们,触发代码中 form的submit即可.
此时,如果//www.php.com/test.php,有写入Cookie的操作.则可以保证第三方Cookie被写入.

默认不禁止第三方cookie的浏览器测试:

测试为在下列浏览器中,禁止第三方Cookie,并配置简洁P3P策略.

Firefox下 :
Firefox下禁止第三方Cookie后,很直接, 无论HTTP 还是 JS都无法读写Cookie.

Chrome:
Chrome10 开始 支持用户自定义是否允许在 地址栏: about:flags 中配置 是否允许第三方cookie.
而之前的版本需要通过, 选项-高级选项-隐私权-内容设置-拦截第三方cookie 来配置.

对于chrome9 和之前的Dev版本来说,通过选项配置禁止第三方cookie后, 在配置P3P简洁策略后, JS 可读cookie,但不能写,而 HTTP方式,则都可以.
而chrome10+ 无论选择什么方式设置, 只允许HTTP、JS读,但是不允许写.
而Chrome的非Dev版, 甚至没有提供第三方Cookie的隐私策略选项. 有的仅仅是,关于Cookie的站点允许列表,或者主动访问过的站点的Cookie.
Opera:
通过 工具-首选项-高级-Cookie-仅接受来自我访问站点的Cookie 来设置禁止第三方Cookie.
Opera的有趣之处在于,一但禁止第三方Cookie,则 P3P头毫无意义,而Opera自身的隐私策略则非常有趣,
允许JS 的读写,以及HTTP的读, 但是禁止HTTP 对Cookie的写入.


总结:

 

浏览器 默认允许第三方Cookie 是否支持P3P 禁止第三方Cookie后,配置P3P简明策略头的效果 补充
IE6

HTTP可读写Cookie
JS可读Cookie
首次读到P3P头,JS无写Cookie权限.第二次才OK

(第二次.直接Cache.也不行.除非第一次非Cache并读到p3p头.后面我会提到解决方案.)

避免JS的写操作
IE7-IE9
HTTP、JS,可随意读写. -
FireFox HTTP、JS都不可读写 -
Chrome 部分支持,趋势-否 趋势为HTTP、JS可读不可写. -
Safari HTTP、JS可读不可写 借助Post提交表单,实现写操作.
Opera
JS可读写
HTTP可读不可写.
-

建议:

1. 其实P3P简洁策略,可以最简写成: P3P:CP=. 就OK啦,也就是说IE对P3P简介策略的支持,属于搞笑级别的.根本不看内容,至少对于第三方操作cookie是如此的.
2. IE6的实现有bug.需要注意.首次访问第三方页面,JS无法写入第三方Cookie的bug.建议尽量避免JS对Cookie的写操作.
3. 要搞定Safari,需要借助后台至少配置一个APP,与前台配合.
4. 对于第三方来说,建议避免使用JS操作Cookie,最多用来读,而不是写. 除非是和登录验证有关,否则建议使用Storage代替Cookie的使用.

最后:

如果你非要用在ie6下,用js写cookie. 那么有一个很悲剧的做法.. 服务器端给资源配置可缓存.(包括反向代理和客户端.) 然后想办法在IE6的时候刷新一次页面.这样就ok了. 刷新时一定要用 location.reload(false)  即先忽略客户端缓存.尝试304服务器端校验客户端缓存可靠性..这样做的好处是.即保证了 cookie的写入性. 又保证,如果页面是静态资源.则反向代理的可用性.. 否则还是直接用动态资源,http方式去写好了.  需要注意的是.除了页面刷新.譬如其他方式加载页面资源.试图通过预读其p3p简介策略头.是无效的. 比如 用 script type="text/c" 方式去预读一次. 如果你有这个想法。还是早早放弃的好.