1.点击地址栏头部位置
2. 点击网站设置。
(如果已经有flash,则点击允许即可)
3. 找到flash,点击允许。
说明:
再打开Chrome时,每次都得这么设置。
可以安装MyIE浏览器,对需要flash的网站,直接用MyIE打开即可。
提供谷歌浏览器chrome的操作帮助
1.点击地址栏头部位置
2. 点击网站设置。
(如果已经有flash,则点击允许即可)
3. 找到flash,点击允许。
说明:
再打开Chrome时,每次都得这么设置。
可以安装MyIE浏览器,对需要flash的网站,直接用MyIE打开即可。
构建几乎不会崩溃或挂起的渲染引擎几乎是不可能的。构建完全安全的渲染引擎几乎也是不可能的。
在某种程度上,2006年左右的网络浏览器的状态类似于过去的单用户,协作多任务操作系统。由于此类操作系统中的行为异常的应用程序可能会破坏整个系统,因此Web浏览器中的行为异常的网页也可能因此而崩溃。它只需要一个浏览器或插件错误即可关闭整个浏览器和所有当前运行的选项卡。
现代操作系统更强大,因为它们将应用程序置于相互隔离的单独进程中。一个应用程序中的崩溃通常不会损害其他应用程序或操作系统的完整性,并且每个用户对其他用户数据的访问受到限制。
我们对浏览器选项卡使用单独的过程,以保护整个应用程序免受渲染引擎中的错误和干扰。我们还限制了每个渲染引擎进程对其他进程以及系统其余部分的访问。在某些方面,这为Web浏览带来了内存保护和访问控制带给操作系统的好处。
我们将运行UI并管理选项卡和插件过程的主要过程称为“浏览器过程”或“浏览器”。同样,特定于选项卡的过程称为“渲染过程”或“渲染器”。渲染器使用Blink开源布局引擎来解释和布局HTML。
每个渲染进程都有一个全局RenderProcess
对象,该对象管理与父浏览器进程的通信并维护全局状态。浏览器RenderProcessHost
为每个渲染进程维护一个对应项,该进程管理浏览器状态和渲染器的通信。浏览器和渲染器使用Chromium的IPC系统进行通信。
每个渲染过程都有一个或多个RenderView
对象,由管理RenderProcess
,这些对象对应于内容的选项卡。对应项 在渲染器中RenderProcessHost
维护RenderViewHost
与每个视图对应的内容。每个视图都有一个视图ID,该ID用于区分同一渲染器中的多个视图。这些ID在一个渲染器中是唯一的,但在浏览器中不是唯一的,因此要识别视图,需要一个RenderProcessHost
ID和一个View ID。通过这些RenderViewHost
对象可以完成从浏览器到特定内容选项卡的通信,这些对象知道如何将消息通过它们发送RenderProcessHost
到RenderProcess
和RenderView
。
在渲染过程中:
RenderProcess
使用相应的句柄处理IPC RenderProcessHost
。RenderProcess
每个渲染过程只有一个对象。这就是所有浏览器↔渲染器通信的发生方式。RenderView
对象RenderViewHost
在浏览器进程中(通过RenderProcess)和我们的WebKit嵌入层与其对应的对象通信。此对象表示选项卡或弹出窗口中一个网页的内容在浏览器过程中:
Browser
对象表示顶级浏览器窗口。RenderProcessHost
对象表示单个浏览器↔渲染器IPC连接的浏览器端。有一个RenderProcessHost
在每个渲染过程中的浏览器进程。RenderViewHost
对象封装的通信与远程RenderView
和RenderWidgetHost处理用于输入和绘画RenderWidget在浏览器中。有关此嵌入的工作方式的更多详细信息,请参阅Chromium如何显示网页 设计文档。
通常,每个新窗口或选项卡都会在新过程中打开。浏览器将产生一个新进程,并指示它创建一个RenderView
。
有时有必要或希望在选项卡或窗口之间共享渲染过程。Web应用程序会打开一个新窗口,希望与之进行同步通信,例如,使用 JavaScript中的window.open。在这种情况下,当我们创建新的窗口或选项卡时,我们需要重用打开窗口的过程。如果进程总数太大,或者用户已经打开一个导航到该域的进程,我们还可以为新进程分配新选项卡的策略。这些策略在过程模型中进行了描述。
与浏览器进程的每个IPC连接都会监视进程句柄。如果发出了这些句柄的信号,则表示渲染过程已崩溃,并且向选项卡通知了崩溃。现在,我们显示一个“悲伤的标签”屏幕,通知用户渲染器崩溃了。可以通过按下重新加载按钮或开始新的导航来重新加载页面。发生这种情况时,我们注意到没有任何流程,而是创建一个新的流程。
鉴于渲染器在单独的进程中运行,我们有机会通过sandboxing限制其对系统资源的访问。例如,我们可以确保渲染器对网络的唯一访问是通过其父浏览器进程进行的。同样,我们可以使用主机操作系统的内置权限来限制其对文件系统的访问。
除了限制渲染器对文件系统和网络的访问之外,我们还可以限制对渲染器对用户的显示和相关对象的访问。我们在用户看不见的单独Windows“ 桌面 ” 上运行每个渲染过程。这样可以防止受损的渲染器打开新窗口或捕获击键。
给定渲染器在单独的进程中运行,将隐藏的选项卡视为较低优先级就变得很简单。通常,Windows上最小化的进程会将其内存自动放入“可用内存”池中。在低内存情况下,Windows将在交换出更高优先级的内存之前将该内存交换到磁盘上,从而有助于使用户可见的程序具有更高的响应速度。我们可以将相同的原理应用于隐藏的标签。当渲染进程没有顶级选项卡时,我们可以释放该进程的“工作集”大小,以提示系统在必要时先将该内存换出到磁盘。因为我们发现减小工作集大小也会降低用户在两个选项卡之间切换时的选项卡切换性能,所以我们逐渐释放此内存。这意味着,如果用户切换回最近使用的选项卡,则与最近使用的选项卡相比,该选项卡的内存更有可能被分页。具有足够内存来运行所有程序的用户根本不会注意到此过程:Windows仅会在需要时才真正回收这些数据,因此,如果有足够的内存,则不会对性能造成任何影响。
这有助于我们在低内存情况下获得更好的内存占用。与很少使用的背景标签相关联的内存可以完全换出,而前景标签的数据可以完全加载到内存中。相比之下,单进程浏览器会将所有选项卡的数据随机分布在其内存中,并且不可能如此干净地分离使用和未使用的数据,从而浪费内存和性能。
Firefox风格的NPAPI插件在其自身的进程中运行,与渲染器分开。插件体系结构中对此进行了详细描述。
眨眼的工作原理
作者:haraken @
最后更新:2018年8月14日
状态:PUBLIC
在眨眼上工作并不容易。对于新的Blink开发人员而言,这并不容易,因为已经引入了许多特定于Blink的概念和编码约定来实现非常快速的渲染引擎。即使对于有经验的Blink开发人员来说,这也不容易,因为Blink庞大且对性能,内存和安全性极为敏感。
本文档旨在提供1 万英尺的“ Blink的工作原理”概述,我希望它将有助于Blink开发人员快速熟悉该体系结构:
有关Blink开发的更多常规信息,请参阅Chromium Wiki页面。
Blink是Web平台的渲染引擎。粗略地说,Blink实现了所有在浏览器选项卡中呈现内容的内容:
许多用户(例如Chromium,Android WebView和Opera)通过内容公开API嵌入了Blink 。
从代码库的角度来看,“闪烁”通常是指// third_party / blink /。从项目角度来看,“闪烁”通常表示实现Web平台功能的项目。实现Web平台功能的代码跨越// third_party / blink /,// content / renderer /,// content /浏览器/和其他地方。
铬具有多工艺体系结构。Chromium具有一个浏览器进程和N个沙盒渲染器进程。闪烁在渲染器进程中运行。
创建了多少个渲染器进程?出于安全原因,隔离跨站点文档之间的内存地址区域非常重要(这称为Site Isolation)。从概念上讲,每个渲染器过程最多应专用于一个站点。但是实际上,当用户打开太多标签页或设备没有足够的RAM时,有时将每个渲染器进程限制在一个站点上有时会很繁琐。然后,渲染器进程可以由从不同站点加载的多个iframe或标签共享。这意味着一个选项卡中的iframe可以由不同的渲染器进程托管,而不同选项卡中的iframe可以由相同的渲染器进程托管。渲染器进程,iframe和Tab之间没有1:1映射。
假定渲染器进程在沙箱中运行,则Blink需要要求浏览器进程调度系统调用(例如,文件访问,播放音频)并访问用户配置文件数据(例如Cookie,密码)。这种浏览器-渲染器过程通信是由Mojo实现的。(注意:过去我们使用的是Chromium IPC,但仍然有很多地方在使用它。但是,它已被弃用,并在后台使用Mojo。)在Chromium方面,服务化正在进行中,并将浏览器过程抽象为一组“服务。从Blink角度来看,Blink可以仅使用Mojo与服务和浏览器进程进行交互。
如果您想了解更多信息:
在渲染器进程中创建了多少个线程?
眨眼有一个主线程,N个工作线程和几个内部线程。
几乎所有重要的事情都在主线程上发生。所有JavaScript(工人除外),DOM,CSS,样式和布局计算都在主线程上运行。假设主要是单线程体系结构,Blink进行了高度优化以最大化主线程的性能。
眨眼可能会创建多个工作线程来运行Web Workers,ServiceWorker和Worklets。
Blink和V8可能会创建几个内部线程来处理webaudio,数据库,GC等。
对于跨线程通信,必须使用通过PostTask API传递消息。不建议使用共享内存编程,除了出于性能原因确实需要使用它的几个地方。这就是为什么您在Blink代码库中看不到很多MutexLocks的原因。
如果您想了解更多信息:
眨眼由BlinkInitializer :: Initialize()初始化。在执行任何Blink代码之前必须调用此方法。
另一方面,Blink从未完成。也就是说,渲染器进程被强制退出而不进行清理。原因之一是性能。另一个原因是,通常很难以一种有序的方式清理渲染器进程中的所有内容(这是不值得的工作)。
内容公共API是使嵌入程序嵌入呈现引擎的API层。内容公共API必须小心维护,因为它们会暴露在嵌入程序中。
眨眼公共API是将// // third_party / blink /的功能公开给Chromium的API层。该API层只是从WebKit继承的历史工件。在WebKit时代,Chromium和Safari共享WebKit的实现,因此需要API层才能将WebKit的功能公开给Chromium和Safari。既然Chromium是// third_party / blink /的唯一嵌入者,那么API层就没有意义了。通过将网络平台代码从Chromium移到Blink(该项目称为Onion Soup),我们正在积极减少Blink公共API的数量。
// third_party / blink /具有以下目录。有关这些目录的更详细定义,请参阅此文档:
依存关系按以下顺序流动:
Blink仔细维护暴露于// third_party / blink /的低级基元列表。
如果您想了解更多信息:
WTF是一个“特定于眨眼的基础”库,位于platform / wtf /。我们正在尝试尽可能多地统一Chromium和Blink之间的编码原语,因此WTF应该很小。需要此库是因为确实需要针对Blink的工作量和Oilpan(Blink GC)优化许多类型,容器和宏。如果类型是在WTF中定义的,则Blink必须使用WTF类型而不是// base或std库中定义的类型。最受欢迎的是矢量,哈希集,哈希图和字符串。眨眼应该使用WTF :: Vector,WTF :: HashSet,WTF :: HashMap,WTF :: String和WTF :: AtomicString而不是std :: vector,std :: * set,std :: * map和std :: string 。
如果您想了解更多信息:
就Blink而言,您需要关心三个内存分配器:
您可以使用USING_FAST_MALLOC()在PartitionAlloc的堆上分配一个对象:
类SomeObject {
USING_FAST_MALLOC(SomeObject);
静态std :: unique_ptr <SomeObject> Create(){
返回std :: make_unique <SomeObject>(); //分配在PartitionAlloc的堆上。
}
};
由PartitionAlloc分配的对象的生存期应由scoped_refptr <>或std :: unique_ptr <>管理。强烈建议不要手动管理生命周期。闪烁禁止手动删除。
您可以使用GarbageCollected在Oilpan的堆上分配一个对象:
类SomeObject:公共GarbageCollected <SomeObject> {
静态SomeObject * Create(){
返回新的SomeObject; //分配在Oilpan的堆上。
}
};
Oilpan分配的对象的生存期由垃圾收集自动管理。您必须使用特殊的指针(例如Member <>,Persistent <>)将对象保存在Oilpan的堆上。请参阅此API参考以熟悉有关Oilpan的编程限制。最重要的限制是不允许您在油锅对象的析构函数中触摸任何其他油锅对象(因为无法保证销毁顺序)。
如果您既不使用USING_FAST_MALLOC()也不使用GarbageCollected,则在系统malloc的堆上分配对象。在眨眼中强烈建议不要这样做。所有Blink对象应由PartitionAlloc或Oilpan分配,如下所示:
无论使用PartitionAlloc还是Oilpan,都必须非常小心,不要创建悬空的指针(注意:强烈建议不要使用原始指针)或内存泄漏。
如果您想了解更多信息:
为了提高渲染引擎的响应速度,Blink中的任务应尽可能异步执行。不鼓励同步IPC / Mojo和任何其他可能花费几毫秒的操作(尽管某些操作是不可避免的,例如用户的JavaScript执行)。
呈现器进程中的所有任务都应使用正确的任务类型发布到Blink Scheduler,如下所示:
//使用kNetworking的任务类型将任务发布到框架的调度程序
frame-> GetTaskRunner(TaskType :: kNetworking)-> PostTask(…,WTF :: Bind(&Function));
Blink Scheduler维护多个任务队列,并巧妙地对任务进行优先级排序,以最大化用户感知的性能。重要的是要指定适当的任务类型,以使Blink Scheduler能够正确,智能地调度任务。
如果您想了解更多信息:
页面,框架,文档,ExecutionContext和DOMWindow是以下概念:
渲染过程:页面= 1:N
页:框架= 1:M.
框架:DOMWindow:文档(或ExecutionContext)= 1:1:1在任何时间点,但映射可能随时间而变化。例如,考虑以下代码:
iframe.contentWindow.location.href =“ https://example.com”;
在这种情况下,将为https://example.com创建一个新的DOMWindow和一个新的Document 。但是,可以重复使用该框架。
(注意:确切地说,在某些情况下会创建一个新的Document,但是DOMWindow和Frame会被重用。甚至还有一些更复杂的情况。)
如果您想了解更多信息:
站点隔离使事情变得更加安全,但更加复杂。🙂站点隔离的想法是为每个站点创建一个渲染器进程。(网站是页面的可注册域+ 1标签及其URL方案。例如,https://mail.example.com和https://chat.example.com在同一网站中,但https:// noodles.com和https://pumpkins.com都没有。)如果一个页面包含一个跨站点IFRAME,该页面可以由两个渲染过程托管。考虑以下页面:
<!– https://example.com –>
<身体>
<iframe src =” https://example2.com”> </ iframe>
</ body>
主框架和<iframe>可以由不同的渲染器进程托管。渲染器进程本地的帧由LocalFrame表示,而不是渲染器进程本地的帧由RemoteFrame表示。
从主框架的角度来看,主框架是LocalFrame,而<iframe>是RemoteFrame。从<iframe>的角度来看,主框架是RemoteFrame,而<iframe>是LocalFrame。
本地框架和远程框架(可能存在于不同的渲染器进程中)之间的通信是通过浏览器进程进行处理的。
如果您想了解更多信息:
相框/文档可能处于分离状态。考虑以下情况:
doc = iframe.contentDocument;
iframe.remove(); //将iframe与DOM树分离。
doc.createElement(“ div”); //但是您仍然可以在分离的框架上运行脚本。
棘手的事实是,您仍然可以在分离的框架上运行脚本或DOM操作。由于框架已经分离,大多数DOM操作将失败并引发错误。不幸的是,分离框架上的行为在浏览器之间并不能真正实现互操作,在规范中也没有明确定义。基本上,人们期望JavaScript可以继续运行,但是大多数DOM操作应该会因某些适当的异常而失败,例如:
无效someDOMOperation(…){
if(!script_state _-> ContextIsValid()){//框架已经分离
…;//设置例外等
返回;
}
}
这意味着在通常情况下,当框架分离时,Blink需要执行一系列清理操作。您可以通过从ContextLifecycleObserver继承来做到这一点,如下所示:
类SomeObject:公共GarbageCollected <SomeObject>,公共ContextLifecycleObserver {
void ContextDestroyed()覆盖{
//在此进行清理操作。
}
〜SomeObject(){
//在这里进行清理操作不是一个好主意,因为现在进行清理已经太迟了。此外,不允许析构函数接触Oilpan堆上的任何其他对象。
}
};
当JavaScript访问node.firstChild时,将调用node.h 中的Node :: firstChild()。它是如何工作的?让我们看一下node.firstChild的工作方式。
首先,您需要根据规范定义一个IDL文件:
// node.idl
接口Node:EventTarget {
[…]只读属性Node?第一个孩子;
};
Web IDL的语法在Web IDL规范中定义。[…] 称为IDL扩展属性。一些IDL扩展属性是在Web IDL规范中定义的,而另一些是特定于Blink的IDL扩展属性。除了特定于闪烁的IDL扩展属性外,IDL文件应以符合规范的方式编写(即,仅从规范中复制并粘贴)。
其次,您需要为Node定义一个C ++类,并为firstChild实现一个C ++ getter:
class EventTarget:public ScriptWrappable {//所有暴露给JavaScript的类都必须从ScriptWrappable继承。
…;
};
类Node:public EventTarget {
DEFINE_WRAPPERTYPEINFO(); //所有具有IDL文件的类都必须具有此宏。
节点* firstChild()const {return first_child_; }
};
在通常情况下,就是这样。生成node.idl时,IDL编译器会自动为Node接口和Node.firstChild生成Blink-V8绑定。自动生成的绑定是在//src/out/{Debug,Release}/gen/third_party/blink/renderer/bindings/core/v8/v8_node.h中生成的。当JavaScript调用node.firstChild时,V8会调用v8_node.h中的V8Node :: firstChildAttributeGetterCallback(),然后会调用您在上面定义的Node :: firstChild()。
如果您想了解更多信息:
当您编写涉及V8 API的代码时,了解隔离,上下文和世界的概念很重要。它们分别在代码库中由v8 :: Isolate,v8 :: Context和DOMWrapperWorld表示。
隔离对应于物理线程。隔离:闪烁中的物理线程= 1:1。主线程具有自己的隔离。辅助线程具有其自己的隔离。
上下文对应于全局对象(在使用框架的情况下,它是框架的窗口对象)。由于每个框架都有其自己的窗口对象,因此渲染器进程中存在多个上下文。调用V8 API时,必须确保您使用的是正确的上下文。否则,v8 :: Isolate :: GetCurrentContext()将返回错误的上下文,在最坏的情况下,它将最终导致对象泄漏并导致安全问题。
World是支持Chrome扩展程序的内容脚本的概念。世界与Web标准中的任何内容都不对应。内容脚本希望与网页共享DOM,但是出于安全原因,必须将内容脚本的JavaScript对象与网页的JavaScript堆隔离。(还必须将一个内容脚本的JavaScript堆与另一个内容脚本的JavaScript堆隔离。)为了实现隔离,主线程为网页创建了一个主世界,为每个内容脚本创建了一个隔离世界。主世界和孤立世界可以访问相同的C ++ DOM对象,但是它们的JavaScript对象是孤立的。通过为一个C ++ DOM对象创建多个V8包装器来实现这种隔离。也就是说,每个世界一个V8包装器。
上下文,世界和框架之间有什么关系?
想象一下,在主线程上有N个世界(一个主世界+(N – 1)个孤立世界)。然后,一个框架应具有N个窗口对象,每个窗口对象用于一个世界。上下文是与窗口对象相对应的概念。这意味着,当我们有M个框架和N个世界时,我们有M * N个上下文(但是这些上下文是惰性创建的)。
如果是工人,则只有一个世界和一个全局对象。因此,只有一个上下文。
同样,当您使用V8 API时,应该非常小心使用正确的上下文。否则,您最终将在孤立的世界之间泄漏JavaScript对象并造成安全灾难(例如,来自A.com的扩展程序可以操纵来自B.com的扩展程序)。
如果您想了解更多信息:
//v8/include/v8.h中定义了许多V8 API 。由于V8 API是低级的并且难以正确使用,因此platform / bindings /提供了一堆包装V8 API的帮助程序类。您应该考虑尽可能使用助手类。如果您的代码必须大量使用V8 API,则应将文件放在bindings / {core,modules}中。
V8使用句柄指向V8对象。最常见的句柄是v8 :: Local <>,用于从计算机堆栈指向V8对象。在计算机堆栈上分配v8 :: HandleScope之后,必须使用v8 :: Local <>。v8 :: Local <>不应在机器堆栈之外使用:
void function(){
v8 :: HandleScope范围;
v8 :: Local <v8 :: Object> object =…; // 这是对的。
}
类SomeObject:公共GarbageCollected <SomeObject> {
v8 :: Local <v8 :: Object> object_; //这是错误的。
};
如果要从计算机堆栈外部指向V8对象,则需要使用包装器跟踪。但是,您必须非常小心,不要用它创建参考循环。通常,V8 API很难使用。如果您不确定自己在做什么,请询问blink-review-bindings @。
如果您想了解更多信息:
每个C ++ DOM对象(例如Node)都有其对应的V8包装器。确切地说,每个C ++ DOM对象每个世界都有其对应的V8包装器。
V8包装器对其相应的C ++ DOM对象有很强的引用。但是,C ++ DOM对象仅对V8包装程序具有弱引用。因此,如果您想让V8包装器存活一段时间,则必须明确地做到这一点。否则,将过早收集V8包装器,并且V8包装器上的JS属性将丢失。
div = document.getElementbyId(“ div”);
child = div.firstChild;
child.foo =“酒吧”;
child = null;
GC(); //如果不执行任何操作,则| firstChild |的V8包装器 由GC收集。
assert(div.firstChild.foo ===“ bar”); // …这将失败。
如果我们什么都不做,GC会收集child ,因此child.foo 会丢失。为了使div.firstChild 的V8包装器保持活动状态,我们必须添加一种机制,“ 只要div 所属的DOM树可以从V8到达,则使div.firstChild 的V8包装器保持活动状态”。
有两种方法可以使V8包装器保持活动状态:ActiveScriptWrappable和包装器跟踪。
如果您想了解更多信息:
从将HTML文件发送到Blink到在屏幕上显示像素还有很长的一段路要走。渲染管道的结构如下。
阅读这个出色的资料,以了解渲染管线的每个阶段的功能。(我认为我能写出比甲板更好的解释🙂
如果您想了解更多信息,请联系:GC收集。
assert(div.firstChild.foo ===“ bar”); // …这将失败。
如果我们什么都不做,GC会收集child,因此child.foo会丢失。为了使div.firstChild的V8包装器保持活动状态,我们必须添加一种机制,“只要div所属的DOM树可以从V8到达,则使div.firstChild的V8包装器保持活动状态”。
有两种方法可以使V8包装器保持活动状态:ActiveScriptWrappable和包装器跟踪。
如果您想了解更多信息:
如何管理V8包装器的生命周期:bindings / core / v8 / V8Wrapper.md
如何使用包装程序跟踪:platform / bindings / TraceWrapperReference.md
渲染管线
从将HTML文件发送到Blink到在屏幕上显示像素还有很长的一段路要走。渲染管道的结构如下。
我们使用WebKit开源项目来布局网页。此代码从Apple中提取,并存储在/ third_party / WebKit目录中。WebKit主要由代表核心布局功能的“ WebCore”和运行JavaScript的“ JavaScriptCore”组成。我们仅出于测试目的运行JavaScriptCore,通常我们将其替换为高性能的V8 JavaScript引擎。我们实际上并没有使用Apple称为“ WebKit”的层,它是WebCore和OS X应用程序(例如Safari)之间的嵌入API。为了方便起见,我们通常将Apple的代码统称为“ WebKit”。
在最低级别,我们有WebKit“端口”。这是我们所需的特定于平台的功能的实现,该功能可与独立于平台的WebCore代码对接。这些文件位于WebKit树中,通常位于铬目录中或作为带有铬后缀的文件。我们的许多端口实际上并不是特定于操作系统的:您可以将其视为WebCore的“ Chromium端口”。对于每个平台,某些部分(如字体渲染)必须以不同的方式处理。
与第三方WebKit代码相比,Chromium应用程序使用不同的类型,编码样式和代码布局。WebKit“胶水”为使用Google编码约定和类型的WebKit提供了更方便的嵌入API(例如,我们使用std :: string代替WebCore :: String和GURL代替KURL)。粘合代码位于/ webkit / glue中。胶合对象的名称通常类似于WebKit对象,但开头带有“ Web”。例如,WebCore :: Frame变为WebFrame。
WebKit的“胶水”层将其余的Chromium代码库与WebCore数据类型隔离开来,以帮助最小化WebCore更改对Chromium代码库的影响。因此,Chromium永远不会直接使用WebCore数据类型。当Chromium需要戳入某些WebCore对象时,API会被添加到WebKit的“胶水”中。
“测试外壳”应用程序是一个准系统的Web浏览器,用于测试我们的WebKit端口和粘合代码。它使用与Chromium相同的粘合接口与WebKit进行通信。它为开发人员提供了一种更简单的方法来测试新代码,而无需具有许多复杂的浏览器功能,线程和进程。此应用程序还用于运行自动化的WebKit测试。但是,“测试外壳”的缺点是,它不像Chromium那样以多进程方式使用WebKit。内容模块嵌入在称为“内容外壳”的应用程序中,该应用程序将很快运行测试。
渲染器中最重要的类是RenderView,它位于/content/renderer/render_view_impl.cc中。该对象表示一个网页。它处理往返于浏览器进程的所有与导航有关的命令。它源自提供绘画和输入事件处理的RenderWidget。所述的RenderView经由全球浏览器进程通信(每呈现进程)RenderProcess对象。
每个渲染器都有两个线程(有关图表,请参见多进程体系结构页面;有关如何编程,请参见Chromium中的线程)。渲染线程是主要对象(例如RenderView)所在的位置并运行所有WebKit代码。当它与浏览器通信时,消息首先发送到主线程,然后主线程将消息分派给浏览器进程。除其他外,这使我们能够将消息从渲染器同步发送到浏览器。发生这种情况的原因是少数操作需要浏览器的结果才能继续。一个示例是在JavaScript请求时获取页面的Cookie。渲染器线程将阻塞,并且主线程将对所有收到的消息进行排队,直到找到正确的响应为止。随后将任何同时接收到的消息发布到渲染器线程以进行正常处理。
与渲染过程的所有IPC通信都是在浏览器的I / O线程上完成的。该线程还处理所有网络通信,以防止其干扰用户界面。
在主线程(运行用户界面的地方)上初始化RenderProcessHost时,它将创建新的渲染器进程和带有指向渲染器的命名管道的ChannelProxy IPC对象。该对象在浏览器的I / O线程上运行,侦听到渲染器的命名管道,并自动将所有消息转发回UI线程上的RenderProcessHost。甲ResourceMessageFilter将被安装在该通道中,这将筛选出可直接在I / O线程上处理诸如网络请求的某些消息。此过滤发生在ResourceMessageFilter :: OnMessageReceived中。
所述RenderProcessHost在UI线程上是负责分派所有视图专用消息到适当的RenderViewHost(它处理非观看专用消息本身的有限的数目)。这种调度发生在RenderProcessHost :: OnMessageReceived中。
特定于视图的消息进入RenderViewHost :: OnMessageReceived。大多数消息都在这里处理,其余消息转发到RenderWidgetHost基类。这两个对象映射到渲染器中的RenderView和RenderWidget(有关这些含义,请参见上面的“ Render Process”)。每个平台都有一个视图类(RenderWidgetHostView [Aura | Gtk | Mac | Win]),以实现与本机视图系统的集成。
在RenderView / Widget上方是WebContents对象,大多数消息实际上最终都作为对该对象的函数调用而结束。一个WebContents代表网页的内容。它是内容模块中的顶级对象,并负责以矩形视图显示网页。有关更多信息,请参见内容模块页面。
所述WebContents对象被包含在TabContentsWrapper。那是chrome /并负责制表符。
说明性例子
围绕Chromium源代码 涵盖了导航和启动的其他示例。
设置光标是从渲染器发送到浏览器的典型消息的示例。在渲染器中,将发生以下情况。
然后浏览器控制:
发送鼠标单击是从浏览器到渲染器的典型消息示例。
请注意,WebContents中还会创建许多其他类型的消息,尤其是导航消息。它们遵循从WebContents到RenderViewHost的相似路径。
然后渲染器控制:
通过Kayce 巴斯克技术作家,Chrome DevTools&Lighthouse
在这个全面的Chrome DevTools网络分析功能参考中,探索分析页面加载方式的新方法。注意:此参考文献基于Chrome 58.如果您使用其他版本的Chrome,则DevTools的用户界面和功能可能会有所不同。检查chrome://help
您正在运行的Chrome版本。
默认情况下,只要DevTools处于打开状态,DevTools就会在“网络”面板中记录所有网络请求。
要停止录制请求:
单击“网络”面板上的“ 清除 ”以清除“请求”表中的所有请求。
要跨页面加载保存请求,请选中“网络”面板上的“ 保留日志”复选框。DevTools保存所有请求,直到您禁用 保留日志。
捕获屏幕截图以分析用户在等待页面加载时看到的内容。
要启用屏幕截图,请单击“网络”面板上的“ 捕获屏幕截图 ”。启用后会变为蓝色。
在“网络”面板处于焦点时重新加载页面以捕获屏幕截图。
捕获后,您可以通过以下方式与屏幕截图进行交互:
要重播XHR请求,请右键单击“请求”表中的请求,然后选择“ 重播XHR”。
要模拟首次使用者体验您网站的方式,请选中“ 禁用缓存”复选框。DevTools禁用浏览器缓存。这更准确地模拟了首次用户的体验,因为请求是在重复访问时从浏览器缓存提供的。
如果要在其他DevTools面板中工作时禁用缓存,请使用“网络条件”抽屉。
要随时手动清除浏览器缓存,请右键单击“请求”表中的任意位置,然后选择“ 清除浏览器缓存”。
有一类新的Web应用程序,称为Progressive Web Apps,可以在服务工作者的帮助下脱机运行。在构建此类应用程序时,能够快速模拟没有数据连接的设备非常有用。
选中“ 脱机”复选框以模拟完全脱机的网络体验。
从Network Throttling 菜单模拟2G,3G和其他连接速度。
您可以从各种预设中进行选择,例如Regular或Good 2G。您还可以通过打开“网络限制”菜单并选择“ 自定义” >“ 添加”来添加自己的自定义预设。
DevTools在“ 网络”选项卡旁边显示一个警告图标,提醒您已启用限制。
如果要在其他DevTools面板中工作时节流网络连接,请使用“网络条件”抽屉。
要随时手动清除浏览器cookie,请右键单击“请求”表中的任意位置,然后选择“ 清除浏览器Cookie”。
要手动覆盖用户代理:
使用“ 过滤器”文本框按属性过滤请求,例如请求的域或大小。
如果您看不到文本框,则可能隐藏了“过滤器”窗格。请参见隐藏过滤器窗格。
通过使用空格分隔每个属性,可以同时使用多个属性。例如,mime-type:image/gif larger-than:1K
显示大于一千字节的所有GIF。这些多属性过滤器等同于AND操作。目前不支持OR操作。
以下是支持的属性的完整列表。
domain
。仅显示指定域中的资源。您可以使用通配符(*
)来包含多个域。例如,*.com
显示以所有域名结尾的资源.com
。DevTools使用它遇到的所有域填充自动完成下拉菜单。has-response-header
。显示包含指定HTTP响应标头的资源。DevTools使用它遇到的所有响应标头填充自动完成下拉列表。is
。使用is:running
找WebSocket
资源。larger-than
。显示大于指定大小的资源(以字节为单位)。设置值1000
等于设置值1k
。method
。显示通过指定的HTTP方法类型检索的资源。DevTools使用它遇到的所有HTTP方法填充下拉列表。mime-type
。显示指定MIME类型的资源。DevTools使用它遇到的所有MIME类型填充下拉列表。mixed-content
。显示所有混合内容资源(mixed-content:all
)或仅显示当前显示的资源(mixed-content:displayed
)。scheme
。显示通过不受保护的HTTP(scheme:http
)或受保护的HTTPS(scheme:https
)检索的资源。set-cookie-domain
。显示具有Set-Cookie
标题的资源,该标题具有Domain
与指定值匹配的属性。DevTools使用它遇到的所有cookie域填充自动完成。set-cookie-name
。显示具有Set-Cookie
名称与指定值匹配的标头的资源。DevTools使用它遇到的所有cookie名称填充自动完成。set-cookie-value
。显示具有Set-Cookie
与指定值匹配的值的标头的资源。DevTools使用它遇到的所有cookie值填充自动完成。status-code
。仅显示HTTP状态代码与指定代码匹配的资源。DevTools使用它遇到的所有状态代码填充自动完成下拉菜单。要按请求类型筛选请求,请单击“网络”面板上的XHR,JS,CSS, Img,媒体,字体,文档,WS(WebSocket),清单或其他(此处未列出的任何其他类型)按钮。
如果您看不到这些按钮,则可能隐藏了“过滤器”窗格。请参见隐藏过滤器窗格。
要同时启用多个类型过滤器,请按住Command (Mac)或Control(Windows,Linux),然后单击。
单击并在“概述”窗格中向左或向右拖动,仅显示在该时间范围内处于活动状态的请求。过滤器是包含在内的。显示在突出显示的时间内处于活动状态的任何请求。
数据URL是嵌入到其他文档中的小文件。您在Requests表中看到的任何请求 data:
都是数据URL。
选中隐藏数据URL复选框以隐藏这些请求。
默认情况下,“请求”表中的请求按启动时间排序,但您可以使用其他条件对表进行排序。
单击“请求”中任何列的标题,以按该列对请求进行排序。
要更改Waterfall排序请求的方式,请右键单击“请求”表的标题,将鼠标悬停在“ 瀑布”上,然后选择以下选项之一:
这些描述假设每个相应的选项从最短到最长排名。单击Waterfall列的标题可以反转顺序。
只要DevTools处于打开状态,它就会在“网络”面板中记录所有请求。使用“网络”面板分析请求。
使用“请求”表查看DevTools打开时所做的所有请求的日志。单击或将鼠标悬停在请求上会显示有关它们的更多信息。
“请求”表默认显示以下列:
右键单击“请求”表的标题,然后选择隐藏或显示它的选项。当前显示的选项旁边有复选标记。
要将自定义列添加到“请求”表,请右键单击“请求”表的标题,然后选择“ 响应标题” >“ 管理标题列”。
使用瀑布查看彼此相关的请求时间。默认情况下,瀑布按请求的开始时间进行组织。因此,远离左侧的请求比远离右侧的请求更早开始。
请参阅按活动阶段排序,以查看可以对瀑布进行排序的不同方法。
要查看WebSocket连接的框架:
要刷新表,请在“请求”表的“ 名称”列下重新单击WebSocket连接的 名称。
该表包含三列:
消息根据其类型进行颜色编码:
要查看响应正文的预览:
此选项卡主要用于查看图像。
要查看请求的响应正文:
要查看有关请求的HTTP标头数据:
默认情况下,“标题”选项卡按字母顺序显示标题名称。要按收到的顺序查看HTTP标头名称:
要以人类可读的格式查看URL的查询字符串参数:
要查看请求的查询字符串参数源:
要以人类可读的格式查看查询字符串参数,但保留了编码:
要查看请求的HTTP标头中发送的cookie:
有关每个列的说明,请参阅字段。
要查看请求的时间细分:
请参阅预览时序细分,以便更快地访问此数据。
有关您可能在“时序”选项卡中看到的每个阶段的详细信息,请参阅时序分解阶段。
以下是有关每个阶段的更多信息。
有关访问此视图的另一种方法,请参阅查看时间细分。
要查看请求的计时细分预览,请将鼠标悬停在“请求”表的“ 瀑布”列中的请求条目上。
请参阅查看请求的时间细分,以获取访问此数据的方法,该方法不需要悬停。
以下是有关您在“时序”选项卡中可能会看到的每个阶段的更多信息:
要查看请求的启动器和依赖项,Shift 请在“请求”表中按住并将鼠标悬停在请求上。DevTools将启动器颜色设置为绿色,依赖关系设置为红色。
当Requests表按时间顺序排序时,您悬停在请求上方的第一个绿色请求是依赖项的发起者。如果上面有另一个绿色请求,则该更高的请求是发起者的发起者。等等。
DevTools 在“网络”面板上的多个位置显示DOMContentLoaded
和load
事件的时间。该DOMContentLoaded
事件为蓝色,load
事件为红色。
DOMContentLoaded
和 load
事件 的位置“网络”面板底部的“摘要”窗格中列出了请求总数。警告:此数字仅跟踪自DevTools打开以来记录的请求。如果在DevTools打开之前发生了其他请求,则不会计算这些请求。
请求的总下载大小列在“网络”面板底部的“摘要”窗格中。警告:此数字仅跟踪自DevTools打开以来记录的请求。如果在DevTools打开之前发生了其他请求,则不会计算这些请求。
请参阅查看资源的未压缩大小以查看浏览器解压缩后资源的大小。
当JavaScript语句导致请求资源时,将鼠标悬停在Initiator 列上以查看导致请求的堆栈跟踪。
单击“ 使用大型请求行” ,然后查看“ 大小”列的底部值。
jquery-bundle.js
通过网络发送的文件30.9 KB
的压缩大小是,而未压缩的大小是 86.3 KB
要将所有网络请求保存到HAR文件:
获得HAR文件后,可以将其导入DevTools进行分析。只需将HAR文件拖放到“请求”表中即可。另请参阅HAR Analyzer。
在“ 请求”表的“ 名称”列下,右键单击某个请求,将鼠标悬停在“ 复制”上,然后选择以下选项之一:
展开或折叠“网络”面板UI的各个部分,以关注对您来说重要的内容。
默认情况下,DevTools显示“ 过滤器”窗格。单击“ 过滤” 以隐藏它。
如果需要在网络请求表中添加更多空格,请使用大行。使用大行时,某些列还提供了更多信息。例如,“ 大小” 列的底部值是请求的未压缩大小。
单击“ 使用大型请求行” 以启用大行。
默认情况下,DevTools显示“ 概述”窗格。单击隐藏概述 以隐藏它。
修改了27项功能:
JavaScriptA description property is being added to Symbol.prototype. This provides a more ergonomic way of accessing the description of a Symbol. Previously, the description could be only be accessed indirectly through the Symbol.protoype.toString().
SecurityTLS 1.3 is an overhaul of the TLS protocol with a simpler, less error-prone design that improves both efficiency and security. The new design reduces the number of round-trips required to establish a connection and removes legacy insecure options, making it easier to securely configure a server. It additionally encrypts more of the handshake and makes the resumption mode more resilient to key compromise.
CSSThe default style of the <rp> element is changed to “display:none” instead of “display:inline” even if it is not inside the <ruby>element as defined in HTML specification. This behavior is implemented in the UA style sheet, but the web author can override it.
Behavior in other browsers:
– Edge: display:inline (outside <ruby>), display:none (inside <ruby>)
– Firefox: display:none
– Safari: display:inline, display:none (inside <ruby>)
DOMTo avoid confusion on touch feature detection, ontouch* members on window, document, and element are disabled by default on desktop devices (Mac, Windows, Linux, ChromeOS). Note that this is not disabling touches, and usage such as `addEventListener(“touchstart”, …)` is not being affected.
CSSThis updates the behavior of percentage row tracks and gutters in grid containers with indefinite heights. Previously, these were behaving similarly to percentage heights in regular blocks, but the CSS WG has resolved to make them behave the same as for columns, making them symmetric. Percentages are now ignored when computing intrinsic height and resolved afterwards against that height. That way both column and row axes will have symmetric behavior to resolve percentages tracks and gutters.
DeviceWebUSB is enabled inside dedicated worker contexts. This allows developers to perform heavy I/O and processing of data from a USB device on a separate thread to reduce the performance impact on the main thread.
Network / ConnectivityPriority Hints provide developers a way to indicate a resource’s relative importance to the browser, allowing more control over the order resources are loaded.
Many factors influence a resource’s priority in browsers. These include type, visibility, and preload status of a resource. Priority Hints introduces a developer-set “importance” attribute allowing developers to influence the computed priority of a resource. Supported importance values are auto, low, and high.
Web RTCThis change implements getConfiguration() according to the WebRTC 1.0. Specifically it returns the last configuration applied via setConfiguration(), or if setConfiguration() hasn’t been called, the configuration the RTCPeerConnection was constructed with.
SecurityAppCache is now removed from insecure contexts. AppCache is a powerful feature that allows offline and persistent access to an origin, which is a powerful privilege escalation for an XSS. This will remove that attack vector by only allowing it over HTTPS.
This feature was deprecated in Chrome 67.
DOMDeprecate and remove HTMLFrameSetElement’s anonymous getter which is non-standard.
Network / ConnectivityThe OS build number (for example, “NJH47F” or “OPM4.171019.021.D1” on Android) has been removed from the user-agent identification (User-Agent header and navigator.userAgent) on Android and on iOS. The iOS change follows Safari’s implementation and freezes the build number as “15E148” instead of removing it.
This will prevent abuses of that information such as exploit targeting and fingerprinting. It’ll also bring Chrome closer in line with RFC 7231 section 5.5.3.
Web ComponentsChrome and other browsers implemented the new version, see https://www.chromestatus.com/features/4667415417847808.
V0 is deprecated at M70, and will be removed in M73, around, April 2019.
If you are still using this consider migrating to the new API or upgrading your Polymer library. Use –disable-blink-features=ShadowDOMV0 for testing if your site works without Shadow DOM V0 APIs.
For more info: https://groups.google.com/a/chromium.org/d/msg/blink-dev/h-JwMiPUnuU/sl79aLoLBQAJ
MultimediaPhotos and images constitute the largest chunk of the Web, and many include recognisable features, such as human faces, text, or QR codes. Detecting these features is computationally expensive, but, particularly on mobile devices, hardware manufacturers have long been supporting these features. This API allows accessing hardware-accelerated detectors where available. This is expected to be in origin trials in Chrome 70.
MultimediaOpus is an audio codec already supported by the HTML5 src attribute on <url> elements. This applies to mp4, ogg, and webm containers as well as in webm containers using Media Source Extensions. This change adds support for the Opus codec in the mp4 container to MSE.
MultimediaThis change adds the SourceBuffer.changeType() method to improve cross-codec or cross-bytestream transitions during playback with Media Source Extensions.
JavaScript`globalThis` enables a universal mechanism to access the global object even in strict functions or modules, regardless of the platform.
这是一系列教程中的第一篇,它们将教您Web开发的基础知识。您还将了解一组名为Chrome DevTools的Web开发人员工具,可以提高您的工作效率。
在这个特定的教程中,您将了解HTML和DOM。HTML是Web开发的核心技术之一。它是控制网页结构和内容的语言。DOM也与网页的结构和内容有关,但您稍后会对此有所了解。
您将通过实际构建自己的网站来学习Web开发。当您完成DevTools for Beginners系列中的所有教程时,您完成的站点将如图1所示。
在本教程结束时,您将了解:
你还有一个真正的网站!您可以使用此站点来托管您的简历或博客。
在尝试本教程之前,请完成以下先决条件:
您将在名为Glitch的在线代码编辑器中构建您的站点。
你的网站很空。请按照以下步骤添加一些内容!
<!-- You're "About Me" will go here. -->
为<h1>About Me</h1>
。
... <body> <p> 您的网站!</ p> <main> <h1> 关于我</ h1> </ main> ...
About Me
在页面上可见。它比文本的其余部分大,因为<h1>
元素代表一个部分标题。您的Web浏览器会自动为较大字体的标题设置样式。<p>I am learning HTML. Recent accomplishments:</p>
在下面的行中插入<h1>About Me</h1>
。
... <body> <p> 您的网站!</ p> <main> <h1> 关于我</ h1> <p> 我正在学习网页开发。最近的成就:</ p> </ main> ...
... <p> 我正在学习网络开发。最近的成就:</ p> <ul> <li> 了解如何在Glitch中设置我的代码。</ li> <li> 向我的HTML添加了内容。</ li> <li> TODO:了解如何使用Chrome DevTools试验内容更改。</ li> <li> TODO:了解HTML和DOM之间的区别。</ li> </ ul> ......
如果您正在开发包含大量HTML的大页面,您可以想象在编辑器选项卡和实时选项卡之间来回数百次以查看更改可能有点单调乏味,特别是如果您不知道究竟要放在页面上的是什么。Chrome DevTools可以帮助您体验内容更改,而无需离开实时标签。
在您开始从Chrome DevTools编辑内容之前,了解HTML和DOM之间的区别很有帮助。学习的最佳方式是通过示例:
A new element!?!
。A new element!?!
可以看到 文本index.html
。它不存在!A new element!?!
无处可寻index.html
A new element!?!
,然后选择“ 检查”。DevTools会在您的页面旁边打开。<div>A new element!?!</div>
突出显示为蓝色。虽然DevTools中的这个结构看起来像你的HTML,但实际上它是DOM树。
当您的页面加载时,浏览器会使用您的HTML来创建页面的初始内容。DOM表示页面的当前内容,可以随时间变化。<div>A new element!?!</div
由于<script src="new.js"></script>
HTML底部的标记,神秘内容会添加到您的网页中 。此标记会导致某些JavaScript代码运行。您将在以后的教程中了解有关JavaScript的更多信息,但现在将其视为可以更改页面内容的编程语言。在这种特殊情况下,JavaScript代码会添加<div>A new element!?!</div>
到您的页面中。这就是为什么这个神秘文本在您的实际页面上可见,而不是在您的HTML中。
如果您想在不离开实时标签的情况下快速尝试内容更改,请尝试使用DevTools。
Your site!
并选择“ 编辑为HTML”。<p>Your site!</p>
为下面的代码。
<header> <p> <b> 欢迎来到我的网站!</ b> </ p> <button> 下载我的简历</ button> </ header>
Your site!
已被新内容替换。此工作流程仅适用于试验内容更改。如果您重新加载页面或关闭选项卡,您的更改将永远消失。如果您正在使用此工作流程并且想要保存更改,则需要手动将这些更改复制到HTML。
接下来的几节将向您展示更多可以从DOM树更改内容的方法。
您还可以更改DOM节点的顺序。例如,在您的网页上,导航菜单位于底部附近。将其移至顶部:
<nav>
在DevTools 的DOM树中查找节点。<nav>
节点拖到顶部,这样它就是<body>
节点下面的第一个子节点。该<nav>
节点现在显示在页面顶部。
您还可以从DOM树中删除节点。
<div>A new element!?!</div>
。DevTools突出显示蓝色节点。<div>A new element!?!</div>
节点将从DOM树中删除。你几乎完成。您已在DevTools中对页面进行了一些更改,但它们尚未保存到您的源代码中。
Your site!
返回页面顶部,文本A new element!?!
返回到底部。今天,Adobe宣布计划在2020年底停止支持Flash。
20年来,Flash帮助塑造了您在网络上玩游戏,观看视频和运行应用程序的方式。但在过去的几年里,Flash变得不那么常见了。三年前,80%的桌面Chrome用户每天都会访问一个使用Flash的网站。今天使用率仅为17%并且继续下降。
这一趋势表明,网站正在迁移到开放式网络技术,这种技术比Flash更快,更节能。它们也更安全,因此您可以在购物,银行业务或阅读敏感文档时更安全。它们还适用于移动设备和桌面设备,因此您可以随时随地访问自己喜爱的网站。
这些开放式网络技术成为Chrome去年年底的默认体验,当时网站开始需要您的许可才能运行Flash。Chrome将在未来几年内继续淘汰Flash,首先要求您在更多情况下允许运行Flash,并最终在默认情况下禁用Flash。到2020年底,我们将从Chrome中完全删除Flash。
如果您定期访问今天使用Flash的网站,您可能想知道这会对您产生什么影响。如果站点迁移到打开Web标准,除了您将不再看到在该站点上运行Flash的提示之外,您不会发现太多差异。如果该站点继续使用Flash,并且您授予站点运行Flash的权限,则它将在2020年底之前运行。
与Adobe,其他浏览器和主要发布商进行了大量密切合作,以确保网络已准备好无Flash。我们今天支持Adobe的发布,我们期待与大家合作,让网络变得更好。
借助 Adobe Flash 插件,您可以在计算机上通过 Chrome 收听和观看 Flash 音频和视频内容。
注意:Adobe 将于 2020 年底停止支持 Flash。要想详细了解在 2020 年底之前 Chrome 如何与 Flash 协作,请访问这篇 Chrome 博客。
请仅允许 Flash 在您信任的网站上运行。某些网站可能会利用 Adobe Flash Player 来损害您的计算机。
在退出 Chrome 后,系统不会保存您的 Flash 设置。下次打开 Chrome 时,您必须重新允许之前添加的网站来使用 Flash。
如果某网站无法正常运行,您可能需要更改设置来允许该网站使用 Flash。
如果 Flash 无法正常运行,或者您看到以下错误消息,请尝试按照下文介绍的问题排查步骤操作。
请仅允许 Flash 在您信任的网站上运行。
chrome://components
,然后按 Enter 键。如果 Google 一直是您的默认搜索引擎,但突然不是了,则表明您的系统可能已遭到恶意软件的攻击。获取恢复 Chrome 设置的相关帮助。
如果您已尝试设置搜索引擎但发现无效,则意味着您的计算机上可能存在恶意软件。获取恢复 Chrome 设置的相关帮助。
请输入相应搜索引擎的结果页对应的网址,然后使用 %s
代替搜索字词。
要查找并修改结果页对应的网址,请执行以下操作:
http://www.google.com/search?q=soccer
。%s
。
http://www.google.com/search?q=%s
。