WebKit的历史项管理

标准定义

关于历史的管理,和HTML页面加载一样,都有其相应的标准。地址如下:
WhatWG: https://html.spec.whatwg.org/multipage/browsers.html#history

其中关于历史项的要点如下:

1. 在onload之前,非用户操作引起的导航操作不建立历史项。

非用户操作比如页面中指定的Timer修改location或iframe的src引发的导航操作。而用户点击发起的Timer,则在timer中记录下在发起timer时标记手势来源。但有一个例外是由另一个Timer发起的Timer或是重复执行的Timer, 则仅针对第一次执行有效(以nesting level标识)。

2. 子Frame加载完成仍有未加载的上层Frame, 则不创建历史项。

3. 当前Frame只有一个历史项,且为about:blank, 则不创建历史项。

4. 如果页面跳转的间隔小于1s,则不创建历史项。

关于Timer的nesting level可以参考这里:
http://www.w3.org/html/wg/drafts/html/CR/webappapis.html#timer-nesting-level

以上规则对应于以下三个WebKit的函数:

a. LockBackForwardList NavigationScheduler::mustLockBackForwardList(Frame& targetFrame)
LockBackForwardList NavigationScheduler::mustLockBackForwardList(Frame& targetFrame)
{
    // Non-user navigation before the page has finished firing onload should not create a new back/forward item.
    // See https://webkit.org/b/42861 for the original motivation for this.   
    if (!ScriptController::processingUserGesture() && targetFrame.loader().documentLoader() && !targetFrame.loader().documentLoader()->wasOnloadHandled())
        return LockBackForwardList::Yes;
   
    // Navigation of a subframe during loading of an ancestor frame does not create a new back/forward item.
    // The definition of "during load" is any time before all handlers for the load event have been run.
    // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this.
    for (Frame* ancestor = targetFrame.tree().parent(); ancestor; ancestor = ancestor->tree().parent()) {
        Document* document = ancestor->document();
        if (!ancestor->loader().isComplete() || (document && document->processingLoadEvent()))
            return LockBackForwardList::Yes;
    }
    return LockBackForwardList::No;
}

b. HistoryController::currentItemShouldBeReplaced() const
{
    // From the HTML5 spec for location.assign():
    //  "If the browsing context‘s session history contains only one Document,
    //   and that was the about:blank Document created when the browsing context
    //   was created, then the navigation must be done with replacement enabled."
    return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL());
}


c. void NavigationScheduler::scheduleRedirect(double delay, const URL& url)
{
    if (!shouldScheduleNavigation(url))
        return;
    if (delay < 0 || delay > INT_MAX / 1000)
        return;
    if (url.isEmpty())
        return;
    // We want a new back/forward list item if the refresh timeout is > 1 second.
    if (!m_redirect || delay <= m_redirect->delay()) {
        LockBackForwardList lockBackForwardList = delay <= 1 ? LockBackForwardList::Yes : LockBackForwardList::No;
        schedule(std::make_unique<ScheduledRedirect>(delay, m_frame.document()->securityOrigin(), url, LockHistory::Yes, lockBackForwardList));
    }
}

页面问题分析

1. Timer问题

Timer最大的问题来源于Timer Nesting Level的认定。最初WebKit建一个全局的变量记录, 容易受到不同页面,frame中Timer的影响。

比如用户点击一条链接就使用一个Timer跳转,如果这个Timer创建时,恰好有另一个repeat timer还在循环执行, 且未加载完成(onload事件没有执行), 那它就无法创建历史项,因为这时全局的nesting level大于1,将不会记录为手势触发的跳转。WebKit提交的(https://bugs.webkit.org/show_bug.cgi?id=136401 [^]), 本意用于避免多线程访问的问题,也刚好解决了嵌套层次错误的问题。

现在仍然有一种情况下,这个问题还会存在。见 https://bugs.webkit.org/show_bug.cgi?id=137631。如下面测试页面所示,当在一个document中,有一个repeat timer执行后, 且未加载完成(onload事件没有执行),其它Timer的跳转仍然无法创建历史项。

<html>
  <head>
  </head>
  <body>
  <input type="button" value="Goto next page" onclick="gotoNextPage();">
  <input type="button" value="Start repeating timer" onclick="startRepeatingTimer();">
  <p>
  <div id="Timer">Paused</div>
  </p>
  <script type="text/javascript">

function gotoNextPage(){
    setTimeout(function(){location.href="http://www.webkit.org/";},300);
  }

function startRepeatingTimer(){
    setInterval(function(){document.getElementById("Timer").innerHTML="Running...";},500);
  }
  </script>

<img src="http://therecoveringpolitician.com/wp-content/uploads/2013/01/moderate.jpg" width="640" height="480">
  <img src="http://cullogo.com/full/wallpapers-high-resolution-widescreen-hd-pk.jpg" width="640" height="480">
  <img src="http://www.highresolutionwallpapers.net/wallpapers/autumn-1680x1050.jpg" width="640" height="480">
  </body>
</html>

2. 加载规则导致多余历史项问题

有点页面(华声论坛)由于页面在iframe的初始化及动态改变时,看起来两个src是相同的,都带有ampersand, 一个由HTML指定,另一个由JavaScript设定。

问题在于JavaScript不会转换ampersand, 这样就相当于iframe加载了两个不同的页面,从而产生了两个历史项。

使用下面的测试页面可以重现这个问题, 注意页面中两处http://m.baidu.com的写法:

<html>
  <head>
  </head>
  <body>
  <p>
  <div id="info">Loading...</div>
  </p>
  <script type="text/javascript">
    setTimeout(function(){ attr("sub_iframe","http://m.baidu.com/?abc=23&amp;t=20080225");},5000);

function attr(id,url){
        var obj=document.getElementById(id);
        obj.setAttribute("src", url);
    }
    
    window.onload=function(){
        document.getElementById("info").innerHTML="Loaded.";
    }

</script>
  <iframe src="http://m.baidu.com/?abc=23&amp;t=20080225" id="sub_iframe" width="320" height="480"></iframe><br/>
  </body>
</html>

时间: 2024-11-09 01:53:01

WebKit的历史项管理的相关文章

WebKit历史项管理的实现

历史项管理根据标准定义,由Page管理一个Joint Session History, 包含了各个子Frame的历史项.逻辑上对应如下的关系: 从上面看三个层次:Page,Frame,以及JS Binding的接口.页面加载的核心是由Frame通过FrameLoader来完成的,HistoryController及BackForwardController可以视为页面加载进行历史项操作的接口. Frame层次中通过HistoryController, Page层次中通过BackForwardCo

ASP.NET 5 中的依赖项管理

ASP.NET 5 中的依赖项管理? 提示 本文链接: http://cnblogs.com/qin-nz/p/5034398.html 或 http://blog.qin.nz/aspnet5/aspnet5-dependency-management.html 做过实际开发的都知道,我们需要引用各种各样的类库来帮助我们完成项目的开发. 本文将从服务器端和浏览器端两个方面介绍ASP.NET 5 中的依赖项管理. 服务器端代码使用 Nuget 作为包管理器? 对于已经习惯使用 Visual St

RDIFramework.NET ━ 9.7 操作权限项管理 ━ Web部分

RDIFramework.NET ━ .NET快速信息化系统开发框架 9.7 操作权限项管理 -Web部分 随着经济全球化趋势的发展和企业间竞争的加剧,企业对管理要求不断变化.提高,越来越多的信息都表明权限管理必须向更细.更深的方向发展,其目的就是为了更好的发挥监督职能,对不相容的资源进行分离与牵制,对重要的事项进行监督与制约,重点突出精细化管理的要求.操作权限管理模块是框架权限控制的核心,贯穿于整个框架应用之中.这里的操作权限不能简单理解为一些常用的增.删.改.查,应进行更深入的分析与挖掘.比

第一周--Centos7中的 at 使用技巧及其历史命令管理和用法

**Centos 7 中的at 使用技巧及其历史命令管理和用法** 我将在本文讲述如下内容: 关于Linux的发行版及不同发行版间的联系和区别 实现晚上20:30自动关机,并提示用户 总结历史命令管理以及用法 总结Linux帮助用法 * ----------------------------- 大片如下 ------------------------------------------------------------------------------------------ 第一章:关

科略教育:《三项管理技能企业CEO不得不修炼》

身为企业CEO,有三项管理技能不得不修炼,标准化.流程化.栅格化,任何一个企业的管理都离不开这三项,越是能把这三项发挥到极致的管理,企业运营状况就愈良好.愈规范,那标准化.流程化.栅格化管理究竟是什么样呢,在企业管理中该如何执行呢? 第一.做管理就是做标准.凡事做标准,凡事有标准.福特汽车第一条自动生产流水线源自于各部位的标准化:风行全球的ISO质量体系,其本质就是由标准展开:最时髦的ERP无非信息.程序标准化的集成:PC作为产业在全球经济领域里迅速崛起,靠的就是标准化激活整个产业链:国际化公司

History API与浏览器历史堆栈管理

阅读目录 History API回顾 History API与业务实践 回顾 移动端开发在某些场景中有着特殊需求,如为了提高用户体验和加快响应速度,常常在部分工程采用SPA架构.传统的单页应用基于url的hash值进行路由,这种实现不存在兼容性问题,但是缺点也有--针对不支持onhashchange属性的IE6-7需要设置定时器不断检查hash值改变,性能上并不是很友好. 而如今,在移动端开发中HTML5规范给我们提供了一个History接口,使用该接口可以自由操纵历史记录.本文并不详细介绍Hi

Linux历史命令管理以及用法

history命令管理及用法总结  命令历史:shell进程会在其会话中保存此前用户提交执行过的命令,可以用它来重复执行命令   ~]# history     包括关机前的命令:     定制history的功能,可通过环境变量实现:       HISTSIZE:shell进程可保留的命令历史的条数:       HISTFILE:持久保存命令历史的文件:       HISTFILESIZE:命令历史文件的大小:       HISTTIMEFORMAT="%F %T ":显示时

Linux命令:history命令历史的管理及用

bash可以保存的过去曾经执行过的命令.当某个用户登录到shell中,会读取该用户家目录中的~/.bash_history文件,并将历史命令列表保存到内存中.当用户退出当前shell时,会将内存中的历史命令列表覆盖至~/.bash_history文件. 我们可以通过# histroy 来查看历史命令.history是bash的内部命令.通过# help history获取帮助. 一.history的常见选项 # history ... # 省略前面 994 man poweroff 995 po

从历史看管理

我们要发掘中国古人的管理智慧,不仅要古为今用,还要推陈出新.以历史的角度反思现代企业管理本质,每个企业都借鉴历史方案,找到分权与制衡的方法. (一)中国古代政府的管理精粹 管理没有新问题,只是问题的表现形式不同而已.从两千多年来王朝的覆灭兴衰的过程中可以找到解决下面三个问题的绝佳答案. 如何在战略层次上处理好总部与分部的关系 如何实现上层对下层的控制和监督 如何平衡管理层级 (二)现代企业应该鉴戒的四种王朝管理模式 封建制度:在这种模式制度下,分部本地化,与总部不再具有共同意志,越来越远,与土著