├── .gitignore ├── CNAME ├── COPYING ├── PKGBUILD ├── README.md ├── _config.yml ├── ext.c ├── general.c ├── main.c ├── makefile ├── testrun.sh ├── wyeb.desktop └── wyeb.png /.gitignore: -------------------------------------------------------------------------------- 1 | wyeb 2 | ext.so 3 | adblock.so 4 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | wyeb.org -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: jun7 2 | pkgname=wyeb-git 3 | pkgver=1.1 4 | pkgrel=3 5 | pkgdesc="A vim-like webkit2gtk browser" 6 | arch=('x86_64') 7 | url="http://wyeb.org/" 8 | license=('GPL3') 9 | depends=('webkit2gtk' 'discount' 'perl-file-mimeinfo') 10 | makedepends=('git') 11 | _branch=master 12 | source=("git+https://github.com/jun7/wyeb.git#branch=$_branch") 13 | md5sums=('SKIP') 14 | 15 | pkgver(){ 16 | cd "$srcdir/wyeb" 17 | printf "%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 18 | } 19 | 20 | prepare() { 21 | cd "$srcdir/wyeb" 22 | git pull --rebase origin $_branch 23 | make clean 24 | } 25 | 26 | build() { 27 | cd "$srcdir/wyeb" 28 | DEBUG=0 29 | make 30 | } 31 | 32 | package() { 33 | cd "$srcdir/wyeb" 34 | install -Dm755 wyeb "$pkgdir/usr/bin/wyeb" 35 | install -Dm755 ext.so "$pkgdir/usr/lib/wyebrowser/ext.so" 36 | install -Dm644 wyeb.png "$pkgdir/usr/share/pixmaps/wyeb.png" 37 | install -Dm644 wyeb.desktop "$pkgdir/usr/share/applications/wyeb.desktop" 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wyeb 2 | 3 | ![Hinting](https://github.com/jun7/wyeb/wiki/img/hinting.png) 4 | 5 | [Screenshot](https://github.com/jun7/wyeb/wiki/img/favicon.png) 6 | / [OwnStyleBookmarks](https://github.com/jun7/wyeb/wiki/img/bookmark.png) 7 | / [ContextMenuInFileManager](https://github.com/jun7/wyeb/wiki/img/contextmenu.jpg) 8 | / [History](https://github.com/jun7/wyeb/wiki/img/history.jpg) 9 | 10 | ### Features 11 | wyeb is inspired by dwb and luakit, so basically usage is similar to them. 12 | 13 | - Editable main page. It is a markdown text containing bookmarks. 14 | **e** key opens it by a text editor. As this, all settings are thrown to text editors. 15 | - Monitored conf files. For example, do `echo "* {color:red \!important}" >> user.css` on the conf dir(key **c**). 16 | It should be applied immediately. 17 | - Settings per URI matched regular expression. **e** on a page adds URI to the conf and opens it. 18 | And another thing, **ctrl-i/s** and **v** switch setting 'set:' can be edited. 19 | - Open actions. Most of actions assigned to keys can be accessed by shell. 20 | For example, context-menu items we added are just shell scripts. 21 | - Suffix. `wyeb X "" ""` spawns a process using different dirs added the suffix 'X' for all data. 22 | - [Hacked Hinting.](https://github.com/jun7/wyeb/wiki/img/hackedhint.png) For pages having javascript. 23 | - [Window List.](https://github.com/jun7/wyeb/wiki/img/windowlist.jpg) Key **z** 24 | - No tab. But keys **J/K/x/X** or button actions. `tabbed wyeb plugto` works though. 25 | Make sure tabbed takes no notice of the reordering of wins 26 | without adding `if(sel != c) focus(c);` to the configure event. 27 | - Rocker gestures and middle button gestures. We can change it even to call a script. 28 | (e.g. mdlbtnleft=spawn sh -c "wyeb // showmsg \`pwd\`") 29 | Of course it is in the 'set;', so we can set it by uri. 30 | - Pointer Mode. **p** makes pure click event for javascript pages. 31 | Also it moves pointer pos(don't **esc** but **p**) used by scroll and keeps pos last clicked for same layout pages. 32 | - Range hinting. **ctrl-r**. Also see hidden files in the menu dir, it has callback interface. 33 | - Misc. related domain only loading, 34 | whiteblack.conf, new window with clipboard text, hinting for callback scripts. 35 | - [Adblock extension](https://github.com/jun7/wyebadblock). 36 | 37 | ### Installation 38 | depends: 39 | 40 | - arch linux: 'webkit2gtk' 'discount' 'perl-file-mimeinfo' 41 | - debian 9.4: libwebkit2gtk-4.0-dev discount libfile-mimeinfo-perl 42 | 43 | 'discount(markdown)' 'perl-file-mimeinfo' are used only in the main.conf 44 | 45 | make 46 | sudo make install 47 | 48 | For testing, make and run without install 49 | 50 | ./testrun.sh 51 | 52 | For arch linux: https://aur.archlinux.org/packages/wyeb-git/ 53 | 54 | ### Usage: 55 | Also there are [Tips](https://github.com/jun7/wyeb/wiki) 56 |
 57 | 
 58 | usage: wyeb [[[suffix] action|""] uri|arg|""]
 59 | 
 60 |   wyeb google.com
 61 |   wyeb new google.com
 62 |   wyeb / new google.com
 63 | 
 64 |   suffix: Process ID.
 65 |     It is added to all directories conf, cache and etc.
 66 |     '/' is default. '//' means $SUFFIX.
 67 |   action: Such as new(default), open, opennew ...
 68 |     Except 'new' and some, without a set of $SUFFIX and $WINID,
 69 |     actions are sent to the window last focused
 70 | 
 71 | mouse:
 72 |   rocker gesture:
 73 |     left press and       -        right: back
 74 |     left press and move right and right: forward
 75 |     left press and move up    and right: raise bottom window and close
 76 |     left press and move down  and right: raise next   window and close
 77 |   middle button:
 78 |     on a link            : new background window
 79 |     on free space        : winlist
 80 |     press and move left  : raise bottom window
 81 |     press and move right : raise next   window
 82 |     press and move up    : go to top
 83 |     press and move down  : go to bottom
 84 |     press and scroll up  : go to top
 85 |     press and scroll down: go to bottom
 86 | 
 87 | context-menu:
 88 |   You can add your own script to context-menu. See 'menu' dir in
 89 |   the config dir, or click 'editMenu' in the context-menu.
 90 |   ISCALLBACK, SUFFIX, WINID, WINSLEN, CURRENTSET, URI, TITLE, FOCUSURI,
 91 |   LINK, LINK_OR_URI, LINKLABEL, LABEL_OR_TITLE,
 92 |   MEDIA, IMAGE, MEDIA_IMAGE_LINK,
 93 |   WINX, WINY, WIDTH, HEIGHT, CANBACK, CANFORWARD,
 94 |   PRIMARY/SELECTION, SECONDARY, CLIPBORAD,
 95 |   DLDIR and CONFDIR are set as environment variables.
 96 |   Available actions are in the 'key:' section below.
 97 |   Of course it supports directories and '.'.
 98 |   '.' hides it from the menu but still available in the accels.
 99 | accels:
100 |   You can add your own keys to access context-menu items we added.
101 |   To add Ctrl-Z to GtkAccelMap, insert '<Primary><Shift>z' to the
102 |   last "" in the file 'accels' in the conf directory assigned 'c'
103 |   key, and remove the ';' at the beginning of the line. alt is <Alt>.
104 | 
105 | key:
106 | #4 - is ctrl
107 | #(null) is only for script
108 | 0 - Escape     : tonormal           : To Normal Mode
109 | 4 - bracketleft: tonormal           : 
110 | 0 - i          : toinsert           : 
111 | 0 - I          : toinsertinput      : To Insert Mode with focus of first input
112 | 0 - p          : topointer          : pp resets damping. Esc clears pos. Press enter/space makes btn press
113 | 0 - P          : topointermdl       : Makes middle click
114 | 4 - p          : topointerright     : right click
115 | 0 - f          : tohint             : 
116 | 0 - F          : tohintnew          : 
117 | 0 - t          : tohintback         : 
118 | 0 - d          : tohintdl           : dl is Download
119 | 0 - T          : tohintbookmark     : 
120 | 4 - r          : tohintrangenew     : Open new windows
121 | 0 - D          : showdldir          : 
122 | 0 - y          : yankuri            : Clipboard
123 | 0 - Y          : yanktitle          : Clipboard
124 | 0 - b          : bookmark           : arg: "" or "uri + ' ' + label"
125 | 0 - B          : bookmarkbreak      : Add line break to the main page
126 | 0 - q          : quit               : 
127 | 0 - Q          : quitall            : 
128 | 0 - j          : scrolldown         : 
129 | 0 - k          : scrollup           : 
130 | 0 - h          : scrollleft         : 
131 | 0 - l          : scrollright        : 
132 | 4 - j          : arrowdown          : 
133 | 4 - k          : arrowup            : 
134 | 4 - h          : arrowleft          : 
135 | 4 - l          : arrowright         : 
136 | 4 - f          : pagedown           : 
137 | 4 - b          : pageup             : 
138 | 4 - d          : halfdown           : 
139 | 4 - u          : halfup             : 
140 | 0 - g          : top                : 
141 | 0 - G          : bottom             : 
142 | 0 - plus       : zoomin             : 
143 | 0 - minus      : zoomout            : 
144 | 0 - equal      : zoomreset          : 
145 | 0 - J          : nextwin            : 
146 | 0 - K          : prevwin            : 
147 | 0 - x          : quitnext           : Raise next win and quit current win
148 | 0 - X          : quitprev           : 
149 | 0 - z          : winlist            : 
150 | 0 - H          : back               : 
151 | 0 - L          : forward            : 
152 | 0 - s          : stop               : 
153 | 0 - r          : reload             : 
154 | 0 - R          : reloadbypass       : Reload bypass cache
155 | 0 - slash      : find               : 
156 | 0 - n          : findnext           : 
157 | 0 - N          : findprev           : 
158 | 0 - asterisk   : findselection      : 
159 | 0 - o          : open               : 
160 | 0 - w          : opennew            : New window
161 | 0 - O          : edituri            : Edit arg or focused link or current page's URI
162 | 0 - W          : editurinew         : 
163 | 0 - colon      : showhelp           : 
164 | 0 - M          : showhistory        : 
165 | 4 - m          : showhistoryall     : 
166 | 0 - m          : showmainpage       : 
167 | 4 - C          : clearallwebsitedata: 
168 | 0 - e          : edit               : Edit current uri conf or mainpage
169 | 0 - E          : editconf           : 
170 | 0 - c          : openconfigdir      : 
171 | 0 - v          : setv               : Use the 'set:v' group
172 | 4 - s          : setscript          : Use the 'set:script' group
173 | 4 - i          : setimage           : set:image
174 | 0 - u          : unset              : 
175 | 0 - a          : addwhitelist       : Add URIs blocked to whiteblack.conf as white list
176 | 0 - A          : addblacklist       : URIs loaded
177 | 4 - e          : textlink           : For text elements in insert mode
178 | 0 - (null)     : set                : Use 'set:' + arg group of main.conf. This toggles
179 | 0 - (null)     : set2               : Not toggle
180 | 0 - (null)     : setstack           : arg == NULL ? remove last : add set without checking duplicate
181 | 0 - (null)     : new                : 
182 | 0 - (null)     : newclipboard       : Open [arg + ' ' +] clipboard text in a new window
183 | 0 - (null)     : newselection       : Open [arg + ' ' +] selection ...
184 | 0 - (null)     : newsecondary       : Open [arg + ' ' +] secondaly ...
185 | 0 - (null)     : findclipboard      : 
186 | 0 - (null)     : findsecondary      : 
187 | 0 - (null)     : tohintopen         : not click but opens uri as opennew/back
188 | 0 - (null)     : openback           : 
189 | 0 - (null)     : openwithref        : Current uri is sent as Referer
190 | 0 - (null)     : download           : 
191 | 0 - (null)     : dlwithheaders      : Current uri is sent as Referer. Also cookies
192 | 0 - (null)     : showmsg            : 
193 | 0 - (null)     : raise              : 
194 | 0 - (null)     : winpos             : x:y
195 | 0 - (null)     : winsize            : w:h
196 | 0 - (null)     : click              : x:y
197 | 0 - (null)     : openeditor         : 
198 | 0 - (null)     : spawn              : arg is called with environment variables
199 | 0 - (null)     : sh                 : sh -c arg with env vars
200 | 0 - (null)     : shjs               : sh(arg2) with javascript(arg)'s $RESULT
201 | 0 - (null)     : shhint             : sh with envs selected by a hint
202 | 0 - (null)     : shrange            : sh with envs selected by ranged hints
203 | 0 - (null)     : shsrc              : sh with src of current page via pipe
204 | 0 - (null)     : cookies            : ` wyeb // cookies $URI 'sh -c "echo $RESULT"' ` prints headers.
205 |   Make sure, the callbacks of wyeb are async.
206 |   The stdout is not caller's but first process's stdout.
207 | 
208 | 
209 |
210 |
211 | 
212 | Copyright 2017-2020 jun7
213 | 
214 | This program is free software: you can redistribute it and/or modify
215 | it under the terms of the GNU General Public License as published by
216 | the Free Software Foundation, either version 3 of the License, or
217 | (at your option) any later version.
218 | 
219 | This program is distributed in the hope that it will be useful,
220 | but WITHOUT ANY WARRANTY; without even the implied warranty of
221 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
222 | GNU General Public License for more details.
223 | 
224 | You should have received a copy of the GNU General Public License
225 | along with this program.  If not, see .
226 | 
227 | 
228 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /ext.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017-2020 jun7@hush.com 3 | 4 | This file is part of wyeb. 5 | 6 | wyeb is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | wyeb is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with wyeb. If not, see . 18 | */ 19 | 20 | 21 | //Make sure JSC is 4 times slower and lacks features we using 22 | //So even JSC is true, there are the DOM funcs left and slow 23 | 24 | #if ! JSC + 0 25 | #undef JSC 26 | #define JSC 0 27 | #endif 28 | 29 | #if JSC 30 | #define let JSCValue * 31 | #else 32 | #define let void * 33 | #endif 34 | 35 | #include 36 | #include 37 | 38 | typedef struct _WP { 39 | WebKitWebPage *kit; 40 | #if JSC 41 | WebKitFrame *mf; 42 | #endif 43 | bool hint; 44 | char *apkeys; 45 | guint hintcb; 46 | char lasttype; 47 | char *lasthintkeys; 48 | char *rangestart; 49 | bool script; 50 | GSList *black; 51 | GSList *white; 52 | GSList *emitters; 53 | 54 | int pagereq; 55 | bool redirected; 56 | char **refreq; 57 | 58 | //conf 59 | GObject *seto; 60 | char *lasturiconf; 61 | char *lastreset; 62 | char *overset; 63 | bool setagent; 64 | bool setagentprev; 65 | } Page; 66 | 67 | #include "general.c" 68 | static void loadconf() 69 | { 70 | if (!confpath) 71 | confpath = path2conf("main.conf"); 72 | 73 | GKeyFile *new = g_key_file_new(); 74 | g_key_file_load_from_file(new, confpath,G_KEY_FILE_NONE, NULL); 75 | initconf(new); 76 | } 77 | static void resetconf(Page *page, const char *uri, bool force) 78 | { 79 | page->setagentprev = page->setagent && !force; 80 | page->setagent = false; 81 | 82 | _resetconf(page, uri, force); 83 | 84 | g_object_set_data(G_OBJECT(page->kit), "adblock", 85 | GINT_TO_POINTER(getsetbool(page, "adblock") ? 'y' : 'n')); 86 | } 87 | 88 | 89 | static GPtrArray *pages; 90 | 91 | static void freepage(Page *page) 92 | { 93 | g_free(page->apkeys); 94 | if (page->hintcb) 95 | g_source_remove(page->hintcb); 96 | g_free(page->lasthintkeys); 97 | g_free(page->rangestart); 98 | 99 | g_slist_free_full(page->black, g_free); 100 | g_slist_free_full(page->white, g_free); 101 | g_slist_free_full(page->emitters, g_object_unref); 102 | g_strfreev(page->refreq); 103 | 104 | g_object_unref(page->seto); 105 | g_free(page->lasturiconf); 106 | g_free(page->lastreset); 107 | g_free(page->overset); 108 | 109 | g_ptr_array_remove(pages, page); 110 | g_free(page); 111 | } 112 | 113 | typedef struct { 114 | bool ok; 115 | bool insight; 116 | let elm; 117 | double fx; 118 | double fy; 119 | double x; 120 | double y; 121 | double w; 122 | double h; 123 | double zi; 124 | } Elm; 125 | 126 | static const char *clicktags[] = { 127 | "INPUT", "TEXTAREA", "SELECT", "BUTTON", "A", "AREA", NULL}; 128 | static const char *linktags[] = { 129 | "A", "AREA", NULL}; 130 | static const char *uritags[] = { 131 | "A", "AREA", "IMG", "VIDEO", "AUDIO", NULL}; 132 | 133 | static const char *texttags[] = { 134 | "INPUT", "TEXTAREA", NULL}; 135 | 136 | static const char *inputtags[] = { 137 | "INPUT", "TEXTAREA", "SELECT", NULL}; 138 | 139 | // input types 140 | /* 141 | static const char *itext[] = { //no name is too 142 | "search", "text", "url", "email", "password", "tel", NULL 143 | }; 144 | static const char *ilimitedtext[] = { 145 | "month", "number", "time", "week", "date", "datetime-local", NULL 146 | }; 147 | */ 148 | static const char *inottext[] = { 149 | "color", "file", "radio", "range", "checkbox", "button", "reset", "submit", 150 | 151 | // unknown 152 | "image", //to be submit 153 | // not focus 154 | "hidden", 155 | NULL 156 | }; 157 | 158 | 159 | #if JSC 160 | static void __attribute__((constructor)) ext22() 161 | { DD("this is ext22\n"); } 162 | 163 | 164 | static JSCValue *pagejsv(Page *page, char *name) 165 | { 166 | return jsc_context_get_value(webkit_frame_get_js_context(page->mf), name); 167 | } 168 | static JSCValue *sdoc(Page *page) 169 | { 170 | static JSCValue *s; 171 | if (s) g_object_unref(s); 172 | return s = pagejsv(page, "document"); 173 | } 174 | 175 | #define invoker(...) jsc_value_object_invoke_method(__VA_ARGS__, G_TYPE_NONE) 176 | #define invoke(...) g_object_unref(invoker(__VA_ARGS__)) 177 | #define isdef(v) (!jsc_value_is_undefined(v) && !jsc_value_is_null(v)) 178 | 179 | #define aB(s) G_TYPE_BOOLEAN, s 180 | #define aL(s) G_TYPE_LONG, s 181 | #define aD(s) G_TYPE_DOUBLE, s 182 | #define aS(s) G_TYPE_STRING, s 183 | #define aJ(s) JSC_TYPE_VALUE, s 184 | 185 | static let prop(let v, char *name) 186 | { 187 | let retv = jsc_value_object_get_property(v, name); 188 | if (isdef(retv)) return retv; 189 | g_object_unref(retv); 190 | return NULL; 191 | } 192 | static double propd(let v, char *name) 193 | { 194 | let retv = jsc_value_object_get_property(v, name); 195 | double ret = jsc_value_to_double(retv); 196 | g_object_unref(retv); 197 | return ret; 198 | } 199 | static char *props(let v, char *name) 200 | { 201 | let retv = jsc_value_object_get_property(v, name); 202 | char *ret = isdef(retv) ? jsc_value_to_string(retv) : NULL; 203 | g_object_unref(retv); 204 | return ret; 205 | } 206 | static void setprop_s(let v, char *name, char *data) 207 | { 208 | let dv = jsc_value_new_string(jsc_value_get_context(v), data); 209 | jsc_value_object_set_property(v, name, dv); 210 | g_object_unref(dv); 211 | } 212 | static char *attr(let v, char *name) 213 | { 214 | let retv = invoker(v, "getAttribute", aS(name)); 215 | char *ret = isdef(retv) ? jsc_value_to_string(retv) : NULL; 216 | g_object_unref(retv); 217 | return ret; 218 | } 219 | 220 | 221 | static void __attribute__((unused)) proplist(JSCValue *v) 222 | { 223 | if (jsc_value_is_undefined(v)) 224 | { 225 | DD(undefined value) 226 | return; 227 | } 228 | 229 | char **ps = jsc_value_object_enumerate_properties(v); 230 | if (ps) 231 | for (char **pr = ps; *pr; pr++) 232 | D(p %s, *pr) 233 | else 234 | DD(no props) 235 | g_strfreev(ps); 236 | } 237 | 238 | #define jscunref(v) if (v) g_object_unref(v) 239 | #define docelm(v) prop(v, "documentElement") 240 | #define focuselm(t) invoke(t, "focus") 241 | #define getelms(doc, name) invoker(doc, "getElementsByTagName", aS(name)) 242 | #define defaultview(doc) prop(doc, "defaultView") 243 | 244 | 245 | //JSC 246 | #else 247 | //DOM 248 | 249 | 250 | #define defaultview(doc) webkit_dom_document_get_default_view(doc) 251 | #define getelms(doc, name) \ 252 | webkit_dom_document_get_elements_by_tag_name_as_html_collection(doc, name) 253 | #define focuselm(t) webkit_dom_element_focus(t) 254 | #define docelm(v) webkit_dom_document_get_document_element(v) 255 | #define jscunref(v) ; 256 | 257 | #define attr webkit_dom_element_get_attribute 258 | #define sdoc(v) webkit_web_page_get_dom_document(v->kit) 259 | 260 | #endif 261 | 262 | static void clearelm(Elm *elm) 263 | { 264 | if (elm->elm) g_object_unref(elm->elm); 265 | } 266 | 267 | static char *stag(let elm) 268 | { 269 | if (!elm) return NULL; 270 | static char *name; 271 | g_free(name); 272 | #if JSC 273 | name = props(elm, "tagName"); 274 | #else 275 | name = webkit_dom_element_get_tag_name(elm); 276 | #endif 277 | 278 | //normally name is uppercase letters but occasionally lower 279 | if (name) for (char *c = name; *c; c++) 280 | *c = g_ascii_toupper(*c); 281 | 282 | return name; 283 | } 284 | static bool attrb(let v, char *name) 285 | { 286 | return !g_strcmp0(sfree(attr(v, name)), "true"); 287 | } 288 | static let idx(let cl, int i) 289 | { 290 | #if JSC 291 | char buf[9]; 292 | snprintf(buf, 9, "%d", i); 293 | return prop(cl, buf); 294 | #else 295 | return webkit_dom_html_collection_item(cl, i); 296 | #endif 297 | 298 | // let retv = jsc_value_object_get_property_at_index(cl, i); 299 | // if (isdef(retv)) 300 | // return retv; 301 | // g_object_unref(retv); 302 | // return NULL; 303 | } 304 | 305 | static let activeelm(let doc) 306 | { 307 | #if JSC 308 | let te = prop(doc, "activeElement"); 309 | #else 310 | let te = webkit_dom_document_get_active_element(doc); 311 | #endif 312 | 313 | if (te && (!g_strcmp0(stag(te), "IFRAME") || !g_strcmp0(stag(te), "BODY"))) 314 | { 315 | jscunref(te); 316 | return NULL; 317 | } 318 | return te; 319 | } 320 | 321 | static void recttovals(let rect, double *x, double *y, double *w, double *h) 322 | { 323 | #if JSC 324 | *x = propd(rect, "left"); 325 | *y = propd(rect, "top"); 326 | *w = propd(rect, "width"); 327 | *h = propd(rect, "height"); 328 | #else 329 | *x = webkit_dom_client_rect_get_left(rect); 330 | *y = webkit_dom_client_rect_get_top(rect); 331 | *w = webkit_dom_client_rect_get_width(rect); 332 | *h = webkit_dom_client_rect_get_height(rect); 333 | #endif 334 | } 335 | 336 | 337 | 338 | //@misc 339 | static void send(Page *page, char *action, const char *arg) 340 | { 341 | D(send to main %s %s, action, arg) 342 | WebKitUserMessage* msg = webkit_user_message_new( 343 | sfree(g_strdup_printf("0:%s:%s", action, arg ?: "")) , NULL); 344 | webkit_web_page_send_message_to_view(page->kit, msg, NULL, NULL, NULL); 345 | } 346 | static bool isins(const char **ary, char *val) 347 | { 348 | if (!val) return false; 349 | for (;*ary; ary++) 350 | if (!strcmp(val, *ary)) return true; 351 | return false; 352 | } 353 | static bool isinput(let te) 354 | { 355 | char *tag = stag(te); 356 | if (isins(inputtags, tag)) 357 | { 358 | if (strcmp(tag, "INPUT")) 359 | return true; 360 | else if (!isins(inottext, sfree(attr(te, "TYPE")))) 361 | return true; 362 | } 363 | return false; 364 | } 365 | static char *tofull(let te, char *uri) 366 | { 367 | if (!te || !uri) return NULL; 368 | #if JSC 369 | char *base = props(te, "baseURI"); 370 | #else 371 | char *base = webkit_dom_node_get_base_uri((WebKitDOMNode *)te); 372 | #endif 373 | 374 | char *ret = g_uri_resolve_relative(base, uri, SOUP_HTTP_URI_FLAGS, NULL); 375 | 376 | g_free(base); 377 | return ret; 378 | } 379 | 380 | //func is void *(*func)(let doc, Page *page) 381 | static void *_eachframes(let doc, Page *page, void *func) 382 | { 383 | void *ret; 384 | if ((ret = ((void *(*)(let doc, Page *page))func)(doc, page))) return ret; 385 | 386 | let cl = getelms(doc, "IFRAME"); 387 | let te; 388 | for (int i = 0; (te = idx(cl, i)); i++) 389 | { 390 | #if JSC 391 | WebKitDOMHTMLIFrameElement *tfe = (void *)webkit_dom_node_for_js_value(te); 392 | let fdoc = webkit_frame_get_js_value_for_dom_object(page->mf, 393 | (void *)webkit_dom_html_iframe_element_get_content_document(tfe)); 394 | #else 395 | let fdoc = webkit_dom_html_iframe_element_get_content_document(te); 396 | #endif 397 | 398 | ret = _eachframes(fdoc, page, func); 399 | 400 | jscunref(fdoc); 401 | jscunref(te); 402 | if (ret) break; 403 | } 404 | g_object_unref(cl); 405 | 406 | return ret; 407 | } 408 | static void *eachframes(Page *page, void *func) 409 | { 410 | return _eachframes(sdoc(page), page, func); 411 | } 412 | 413 | 414 | 415 | //@whiteblack 416 | typedef struct { 417 | int white; 418 | regex_t reg; 419 | } Wb; 420 | static void clearwb(Wb *wb) 421 | { 422 | regfree(&wb->reg); 423 | g_free(wb); 424 | } 425 | static GSList *wblist; 426 | static char *wbpath; 427 | static void setwblist(bool reload) 428 | { 429 | if (wblist) 430 | g_slist_free_full(wblist, (GDestroyNotify)clearwb); 431 | wblist = NULL; 432 | 433 | if (!g_file_test(wbpath, G_FILE_TEST_EXISTS)) return; 434 | 435 | GIOChannel *io = g_io_channel_new_file(wbpath, "r", NULL); 436 | char *line; 437 | while (g_io_channel_read_line(io, &line, NULL, NULL, NULL) 438 | == G_IO_STATUS_NORMAL) 439 | { 440 | if (*line == 'w' || *line =='b') 441 | { 442 | g_strchomp(line); 443 | Wb *wb = g_new0(Wb, 1); 444 | if (regcomp(&wb->reg, line + 1, REG_EXTENDED | REG_NOSUB)) 445 | { 446 | g_free(line); 447 | g_free(wb); 448 | continue; 449 | } 450 | wb->white = *line == 'w' ? 1 : 0; 451 | wblist = g_slist_prepend(wblist, wb); 452 | } 453 | g_free(line); 454 | } 455 | g_io_channel_unref(io); 456 | 457 | if (reload) 458 | send(*pages->pdata, "_reloadlast", NULL); 459 | } 460 | static int checkwb(const char *uri) // -1 no result, 0 black, 1 white; 461 | { 462 | if (!wblist) return -1; 463 | 464 | for (GSList *next = wblist; next; next = next->next) 465 | { 466 | Wb *wb = next->data; 467 | if (regexec(&wb->reg, uri, 0, NULL, 0) == 0) 468 | return wb->white; 469 | } 470 | 471 | return -1; 472 | } 473 | static void addwhite(Page *page, const char *uri) 474 | { 475 | //D(blocked %s, uri) 476 | if (getsetbool(page, "showblocked")) 477 | send(page, "_blocked", uri); 478 | page->white = g_slist_prepend(page->white, g_strdup(uri)); 479 | } 480 | static void addblack(Page *page, const char *uri) 481 | { 482 | page->black = g_slist_prepend(page->black, g_strdup(uri)); 483 | } 484 | static void showwhite(Page *page, bool white) 485 | { 486 | GSList *list = white ? page->white : page->black; 487 | if (!list) 488 | { 489 | send(page, "showmsg", "No List"); 490 | return; 491 | } 492 | 493 | FILE *f = fopen(wbpath, "a"); 494 | if (!f) return; 495 | 496 | if (white) 497 | send(page, "wbnoreload", NULL); 498 | 499 | char pre = white ? 'w' : 'b'; 500 | fprintf(f, "\n# %s in %s\n", 501 | white ? "blocked" : "loaded", 502 | webkit_web_page_get_uri(page->kit)); 503 | 504 | list = g_slist_reverse(g_slist_copy(list)); 505 | for (GSList *next = list; next; next = next->next) 506 | fputs(sfree(g_strdup_printf( 507 | "%c^%s\n", pre, sfree(regesc(next->data)))), f); 508 | 509 | g_slist_free(list); 510 | 511 | fclose(f); 512 | 513 | send(page, "openeditor", wbpath); 514 | } 515 | 516 | 517 | //@textlink 518 | static let tldoc; 519 | #if JSC 520 | static let tlelm; 521 | #else 522 | static WebKitDOMHTMLTextAreaElement *tlelm; 523 | static WebKitDOMHTMLInputElement *tlielm; 524 | #endif 525 | static void textlinkset(Page *page, char *path) 526 | { 527 | let doc = sdoc(page); 528 | let cdoc = docelm(doc); 529 | jscunref(cdoc); 530 | if (tldoc != cdoc) return; 531 | 532 | GIOChannel *io = g_io_channel_new_file(path, "r", NULL); 533 | char *text; 534 | g_io_channel_read_to_end(io, &text, NULL, NULL); 535 | g_io_channel_unref(io); 536 | 537 | #if JSC 538 | setprop_s(tlelm, "value", text); 539 | #else 540 | if (tlelm) 541 | webkit_dom_html_text_area_element_set_value(tlelm, text); 542 | else 543 | webkit_dom_html_input_element_set_value(tlielm, text); 544 | #endif 545 | g_free(text); 546 | } 547 | static void textlinkget(Page *page, char *path) 548 | { 549 | let te = eachframes(page, activeelm); 550 | if (!te) return; 551 | 552 | #if JSC 553 | if (tlelm) g_object_unref(tlelm); 554 | if (tldoc) g_object_unref(tldoc); 555 | tlelm = NULL; 556 | tldoc = NULL; 557 | 558 | if (isinput(te)) 559 | tlelm = te; 560 | #else 561 | 562 | tlelm = NULL; 563 | tlielm = NULL; 564 | 565 | if (!strcmp(stag(te), "TEXTAREA")) 566 | tlelm = (WebKitDOMHTMLTextAreaElement *)te; 567 | else if (isinput(te)) 568 | tlielm = (WebKitDOMHTMLInputElement *)te; 569 | #endif 570 | else 571 | { 572 | send(page, "showmsg", "Not a text"); 573 | return; 574 | } 575 | 576 | let doc = sdoc(page); 577 | tldoc = docelm(doc); 578 | char *text = 579 | #if JSC 580 | props(tlelm, "value"); 581 | #else 582 | tlelm ? 583 | webkit_dom_html_text_area_element_get_value(tlelm) : 584 | webkit_dom_html_input_element_get_value(tlielm); 585 | #endif 586 | 587 | GIOChannel *io = g_io_channel_new_file(path, "w", NULL); 588 | g_io_channel_write_chars(io, text ?: "", -1, NULL, NULL); 589 | g_io_channel_unref(io); 590 | g_free(text); 591 | 592 | send(page, "_textlinkon", NULL); 593 | } 594 | 595 | 596 | //@hinting 597 | #if JSC 598 | static char *getstyleval(let style, char *name) 599 | { 600 | char *ret = NULL; 601 | let retv = invoker(style, "getPropertyValue", aS(name)); 602 | ret = jsc_value_to_string(retv); 603 | g_object_unref(retv); 604 | return ret; 605 | } 606 | #else 607 | #define getstyleval webkit_dom_css_style_declaration_get_property_value 608 | #endif 609 | static bool styleis(let dec, char* name, char *pval) 610 | { 611 | char *val = getstyleval(dec, name); 612 | bool ret = (val && !strcmp(pval, val)); 613 | g_free(val); 614 | 615 | return ret; 616 | } 617 | 618 | static Elm getrect(let te) 619 | { 620 | Elm elm = {0}; 621 | 622 | #if JSC 623 | let rect = invoker(te, "getBoundingClientRect"); 624 | #else 625 | WebKitDOMClientRect *rect = 626 | webkit_dom_element_get_bounding_client_rect(te); 627 | #endif 628 | recttovals(rect, &elm.x, &elm.y, &elm.w, &elm.h); 629 | g_object_unref(rect); 630 | 631 | return elm; 632 | } 633 | 634 | static void _trim(double *tx, double *tw, double *px, double *pw) 635 | { 636 | double right = *tx + *tw; 637 | double pr = *px + *pw; 638 | if (pr < right) 639 | *tw -= right - pr; 640 | 641 | if (*px > *tx) 642 | { 643 | *tw -= *px - *tx; 644 | *tx = *px; 645 | } 646 | } 647 | 648 | static char *makehintelm(Page *page, Elm *elm, 649 | const char* text, int len, double pagex, double pagey) 650 | { 651 | char *tag = stag(elm->elm); 652 | bool center = isins(uritags, tag) && !isins(linktags, tag); 653 | 654 | // char *uri = 655 | // attr(elm->elm, "ONCLICK") ?: 656 | // attr(elm->elm, "HREF") ?: 657 | // attr(elm->elm, "SRC"); 658 | 659 | GString *str = g_string_new(NULL); 660 | 661 | #if JSC 662 | let rects = invoker(elm->elm, "getClientRects"); 663 | let rect; 664 | for (int i = 0; (rect = idx(rects, i)); i++) 665 | { 666 | #else 667 | WebKitDOMClientRectList *rects = webkit_dom_element_get_client_rects(elm->elm); 668 | gulong l = webkit_dom_client_rect_list_get_length(rects); 669 | for (gulong i = 0; i < l; i++) 670 | { 671 | WebKitDOMClientRect *rect = 672 | webkit_dom_client_rect_list_item(rects, i); 673 | #endif 674 | double x, y, w, h; 675 | recttovals(rect, &x, &y, &w, &h); 676 | jscunref(rect); 677 | 678 | _trim(&x, &w, &elm->x, &elm->w); 679 | _trim(&y, &h, &elm->y, &elm->h); 680 | 681 | g_string_append_printf(str, "%d%6.0lf*%6.0lf*%6.0lf*%6.0lf*%3d*%d%s;", 682 | center, 683 | x + elm->fx, 684 | y + elm->fy, 685 | w, 686 | h, 687 | len, i == 0, text); 688 | } 689 | g_object_unref(rects); 690 | 691 | char *ret = g_string_free(str, false); 692 | 693 | // g_free(uri); 694 | 695 | return ret; 696 | } 697 | 698 | 699 | static int getdigit(int len, int num) 700 | { 701 | int tmp = num - 1; 702 | int digit = 1; 703 | while ((tmp = tmp / len)) digit++; 704 | return digit; 705 | } 706 | 707 | static char *makekey(char *keys, int len, int max, int tnum, int digit) 708 | { 709 | char ret[digit + 1]; 710 | ret[digit] = '\0'; 711 | 712 | int llen = len; 713 | while (llen--) 714 | if (pow(llen, digit) < max) break; 715 | 716 | llen++; 717 | 718 | int tmp = tnum; 719 | for (int i = digit - 1; i >= 0; i--) 720 | { 721 | ret[i] = toupper(keys[tmp % llen]); 722 | tmp = tmp / llen; 723 | } 724 | 725 | return g_strdup(ret); 726 | } 727 | 728 | static void trim(Elm *te, Elm *prect) 729 | { 730 | _trim(&te->x, &te->w, &prect->x, &prect->w); 731 | _trim(&te->y, &te->h, &prect->y, &prect->h); 732 | } 733 | static Elm checkelm(let win, Elm *frect, Elm *prect, let te, 734 | bool js, bool notttag) 735 | { 736 | let dec = NULL; 737 | Elm ret = getrect(te); 738 | 739 | double bottom = ret.y + ret.h; 740 | double right = ret.x + ret.w; 741 | if ( 742 | (ret.y < 0 && bottom < 0 ) || 743 | (ret.y > frect->h && bottom > frect->h) || 744 | (ret.x < 0 && right < 0 ) || 745 | (ret.x > frect->w && right > frect->w) 746 | ) 747 | goto retfalse; 748 | 749 | ret.insight = true; 750 | 751 | //elms visibility hidden have size also opacity 752 | #if JSC 753 | dec = invoker(win, "getComputedStyle", aJ(te)); 754 | #else 755 | dec = webkit_dom_dom_window_get_computed_style(win, te, NULL); 756 | #endif 757 | 758 | static char *check[][2] = { 759 | {"visibility", "hidden"}, 760 | {"opacity" , "0"}, 761 | {"display" , "none"}, 762 | }; 763 | for (int k = 0; k < sizeof(check) / sizeof(*check); k++) 764 | if (styleis(dec, check[k][0], check[k][1])) 765 | goto retfalse; 766 | 767 | 768 | ret.zi = atoi(sfree(getstyleval(dec, "z-index"))); 769 | 770 | if (ret.zi > prect->zi || styleis(dec, "position", "absolute")) 771 | trim(&ret, frect); 772 | else 773 | trim(&ret, prect); 774 | 775 | if (js && (ret.h == 0 || ret.w == 0)) 776 | goto retfalse; 777 | 778 | if (js && notttag && !styleis(dec, "cursor", "pointer")) 779 | goto retfalse; 780 | 781 | g_object_unref(dec); 782 | 783 | ret.elm = g_object_ref(te); 784 | ret.fx = frect->fx; 785 | ret.fy = frect->fy; 786 | ret.ok = true; 787 | 788 | return ret; 789 | 790 | retfalse: 791 | clearelm(&ret); 792 | if (dec) g_object_unref(dec); 793 | return ret; 794 | } 795 | 796 | static bool addelm(Elm *pelm, GSList **elms) 797 | { 798 | if (!pelm->ok) return false; 799 | Elm *elm = g_new(Elm, 1); 800 | *elm = *pelm; 801 | if (*elms) for (GSList *next = *elms; next; next = next->next) 802 | { 803 | if (elm->zi >= ((Elm *)next->data)->zi) 804 | { 805 | *elms = g_slist_insert_before(*elms, next, elm); 806 | break; 807 | } 808 | 809 | if (!next->next) 810 | { 811 | *elms = g_slist_append(*elms, elm); 812 | break; 813 | } 814 | } 815 | else 816 | *elms = g_slist_append(*elms, elm); 817 | 818 | return true; 819 | } 820 | 821 | static bool eachclick(let win, let cl, 822 | Coms type, GSList **elms, Elm *frect, Elm *prect) 823 | { 824 | bool ret = false; 825 | 826 | let te; 827 | for (int j = 0; (te = idx(cl, j)); j++) 828 | { 829 | bool div = false; 830 | char *tag = stag(te); 831 | if (isins(clicktags, tag)) 832 | { 833 | Elm elm = checkelm(win, frect, prect, te, true, false); 834 | if (elm.ok) 835 | addelm(&elm, elms); 836 | 837 | jscunref(te); 838 | continue; 839 | } else if (!strcmp(tag, "DIV")) 840 | div = true; //div is random 841 | 842 | Elm elm = checkelm(win, frect, prect, te, true, true); 843 | if (!elm.insight && !div && elm.h > 0) 844 | { 845 | jscunref(te); 846 | continue; 847 | } 848 | 849 | Elm *crect = prect; 850 | #if JSC 851 | let ccl = prop(te, "children"); 852 | let dec = invoker(win, "getComputedStyle", aJ(te)); 853 | #else 854 | WebKitDOMHTMLCollection *ccl = webkit_dom_element_get_children(te); 855 | WebKitDOMCSSStyleDeclaration *dec = 856 | webkit_dom_dom_window_get_computed_style(win, te, NULL); 857 | #endif 858 | jscunref(te); 859 | if ( 860 | styleis(dec, "overflow", "hidden") || 861 | styleis(dec, "overflow", "scroll") || 862 | styleis(dec, "overflow", "auto") 863 | ) 864 | crect = &elm; 865 | 866 | g_object_unref(dec); 867 | 868 | if (eachclick(win, ccl, type, elms, frect, crect)) 869 | { 870 | ret = true; 871 | g_object_unref(ccl); 872 | clearelm(&elm); 873 | continue; 874 | } 875 | g_object_unref(ccl); 876 | 877 | if (elm.ok) 878 | { 879 | ret = true; 880 | addelm(&elm, elms); 881 | } 882 | } 883 | return ret; 884 | } 885 | static char *ctexttext; 886 | static GSList *_makelist(Page *page, let doc, let win, 887 | Coms type, GSList *elms, Elm *frect, Elm *prect) 888 | { 889 | const char **taglist = clicktags; //Cclick 890 | if (type == Clink ) taglist = linktags; 891 | if (type == Curi ) taglist = uritags; 892 | if (type == Cspawn) taglist = uritags; 893 | if (type == Crange) taglist = uritags; 894 | if (type == Ctext ) taglist = texttags; 895 | 896 | if (type == Cclick && page->script) 897 | { 898 | #if JSC 899 | let body = prop(doc , "body"); 900 | let cl = prop(body, "children"); 901 | g_object_unref(body); 902 | #else 903 | WebKitDOMHTMLCollection *cl = webkit_dom_element_get_children( 904 | (WebKitDOMElement *)webkit_dom_document_get_body(doc)); 905 | #endif 906 | eachclick(win, cl, type, &elms, frect, prect); 907 | g_object_unref(cl); 908 | } 909 | else for (const char **tag = taglist; *tag; tag++) 910 | { 911 | let cl = getelms(doc, *tag); 912 | let te; 913 | for (int j = 0; (te = idx(cl, j)); j++) 914 | { 915 | Elm elm = checkelm(win, frect, prect, te, false, false); 916 | jscunref(te); 917 | if (elm.ok) 918 | { 919 | if (type == Ctext) 920 | { 921 | if (!isinput(elm.elm)) 922 | { 923 | clearelm(&elm); 924 | continue; 925 | } 926 | 927 | if (ctexttext) 928 | #if JSC 929 | setprop_s(elm.elm, "value", ctexttext); 930 | #else 931 | webkit_dom_html_input_element_set_value(elm.elm, ctexttext); 932 | #endif 933 | focuselm(elm.elm); 934 | clearelm(&elm); 935 | 936 | g_object_unref(cl); 937 | return NULL; 938 | } 939 | 940 | addelm(&elm, &elms); 941 | } 942 | 943 | } 944 | g_object_unref(cl); 945 | } 946 | 947 | return elms; 948 | } 949 | 950 | static GSList *makelist(Page *page, let doc, let win, 951 | Coms type, Elm *frect, GSList *elms) 952 | { 953 | Elm frectr = {0}; 954 | if (!frect) 955 | { 956 | #if JSC 957 | frectr.w = propd(win, "innerWidth"); 958 | frectr.h = propd(win, "innerHeight"); 959 | #else 960 | frectr.w = webkit_dom_dom_window_get_inner_width(win); 961 | frectr.h = webkit_dom_dom_window_get_inner_height(win); 962 | #endif 963 | frect = &frectr; 964 | } 965 | Elm prect = *frect; 966 | prect.x = prect.y = 0; 967 | 968 | //D(rect %d %d %d %d, rect.y, rect.x, rect.h, rect.w) 969 | elms = _makelist(page, doc, win, type, elms, frect, &prect); 970 | 971 | let cl = getelms(doc, "IFRAME"); 972 | let te; 973 | for (int j = 0; (te = idx(cl, j)); j++) 974 | { 975 | Elm cfrect = checkelm(win, frect, &prect, te, false, false); 976 | if (cfrect.ok) 977 | { 978 | #if JSC 979 | double cx = propd(te, "clientLeft"); 980 | double cy = propd(te, "clientTop"); 981 | double cw = propd(te, "clientWidth"); 982 | double ch = propd(te, "clientHeight"); 983 | #else 984 | double cx = webkit_dom_element_get_client_left(te); 985 | double cy = webkit_dom_element_get_client_top(te); 986 | double cw = webkit_dom_element_get_client_width(te); 987 | double ch = webkit_dom_element_get_client_height(te); 988 | #endif 989 | 990 | cfrect.w = MIN(cfrect.w - cx, cw); 991 | cfrect.h = MIN(cfrect.h - cy, ch); 992 | 993 | cfrect.fx += cfrect.x + cx; 994 | cfrect.fy += cfrect.y + cy; 995 | cfrect.x = cfrect.y = 0; 996 | 997 | #if JSC 998 | //some times can't get content 999 | //let fdoc = prop(te, "contentDocument"); 1000 | //if (!fdoc) continue; 1001 | WebKitDOMHTMLIFrameElement *tfe = 1002 | (void *)webkit_dom_node_for_js_value(te); 1003 | let fdoc = webkit_frame_get_js_value_for_dom_object(page->mf, 1004 | (void *)webkit_dom_html_iframe_element_get_content_document(tfe)); 1005 | 1006 | //fwin can't get style vals 1007 | //let fwin = prop(fdoc, "defaultView"); 1008 | //et fwin = prop(te, "contentWindow"); 1009 | let fwin = g_object_ref(win); 1010 | #else 1011 | WebKitDOMDocument *fdoc = 1012 | webkit_dom_html_iframe_element_get_content_document(te); 1013 | WebKitDOMDOMWindow *fwin = defaultview(fdoc); 1014 | #endif 1015 | 1016 | elms = makelist(page, fdoc, win, type, &cfrect, elms); 1017 | g_object_unref(fwin); 1018 | jscunref(fdoc); 1019 | } 1020 | 1021 | jscunref(te); 1022 | clearelm(&cfrect); 1023 | } 1024 | g_object_unref(cl); 1025 | 1026 | return elms; 1027 | } 1028 | 1029 | static char *hinturi(Coms type, let te, char *uritype) 1030 | { 1031 | char *uri = NULL; 1032 | 1033 | if (type == Curi || type == Cspawn || type == Crange) 1034 | { 1035 | uri = attr(te, "SRC"); 1036 | 1037 | if (!uri) 1038 | { 1039 | #if JSC 1040 | let cl = prop(te, "children"); 1041 | #else 1042 | WebKitDOMHTMLCollection *cl = webkit_dom_element_get_children(te); 1043 | #endif 1044 | let le; 1045 | for (int j = 0; (le = idx(cl, j)); j++) 1046 | { 1047 | if (!g_strcmp0(stag(le), "SOURCE")) 1048 | uri = attr(le, "SRC"); 1049 | 1050 | jscunref(le); 1051 | if (uri) break; 1052 | } 1053 | 1054 | g_object_unref(cl); 1055 | } 1056 | 1057 | if (uri && (type == Cspawn || type == Crange)) 1058 | { 1059 | if (!strcmp(stag(te), "IMG")) 1060 | *uritype = 'i'; 1061 | else 1062 | *uritype = 'm'; 1063 | } 1064 | } 1065 | 1066 | if (!uri) 1067 | uri = attr(te, "HREF"); 1068 | 1069 | return uri; 1070 | } 1071 | static void hintret(Page *page, Coms type, let te, bool hasnext) 1072 | { 1073 | char uritype = 'l'; 1074 | char *uri = hinturi(type, te, &uritype) ?: g_strdup("about:blank"); 1075 | char *label = 1076 | #if JSC 1077 | props(te, "innerText") ?: 1078 | #else 1079 | webkit_dom_html_element_get_inner_text((WebKitDOMHTMLElement *)te) ?: 1080 | #endif 1081 | attr(te, "ALT") ?: 1082 | attr(te, "TITLE"); 1083 | 1084 | #if JSC 1085 | let odoc = prop(te, "ownerDocument"); 1086 | char *ouri = props(odoc, "documentURI"); 1087 | g_object_unref(odoc); 1088 | #else 1089 | WebKitDOMDocument *odoc = webkit_dom_node_get_owner_document((void *)te); 1090 | char *ouri = webkit_dom_document_get_document_uri(odoc); 1091 | #endif 1092 | 1093 | char *suri = tofull(te, uri); 1094 | char *retstr = g_strdup_printf("%c%d%s %s %s", uritype, hasnext, ouri, suri, label); 1095 | send(page, "_hintret", retstr); 1096 | 1097 | g_free(uri); 1098 | g_free(label); 1099 | g_free(ouri); 1100 | g_free(suri); 1101 | g_free(retstr); 1102 | } 1103 | 1104 | static bool makehint(Page *page, Coms type, char *hintkeys, char *ipkeys) 1105 | { 1106 | let doc = sdoc(page); 1107 | page->lasttype = type; 1108 | 1109 | if (type != Cclick) 1110 | { 1111 | #if JSC 1112 | let dtype = prop(doc, "doctype"); 1113 | char *name = NULL; 1114 | if (dtype) 1115 | { 1116 | name = props(dtype, "name"); 1117 | g_object_unref(dtype); 1118 | } 1119 | if (name && strcmp("html", name)) 1120 | { 1121 | g_free(name); 1122 | #else 1123 | WebKitDOMDocumentType *dtype = webkit_dom_document_get_doctype(doc); 1124 | if (dtype && strcmp("html", webkit_dom_document_type_get_name(dtype))) 1125 | { 1126 | #endif 1127 | //no elms may be;P 1128 | send(page, "_hintret", sfree(g_strdup_printf( 1129 | "l0%s ", webkit_web_page_get_uri(page->kit)))); 1130 | 1131 | g_free(ipkeys); 1132 | return false; 1133 | } 1134 | #if JSC 1135 | g_free(name); 1136 | #endif 1137 | } 1138 | 1139 | if (hintkeys) 1140 | { 1141 | g_free(page->lasthintkeys); 1142 | hintkeys = page->lasthintkeys = g_strdup(hintkeys); 1143 | } 1144 | else 1145 | hintkeys = page->lasthintkeys; 1146 | if (strlen(hintkeys ?: "") < 3) hintkeys = HINTKEYS; 1147 | 1148 | GFA(page->apkeys, ipkeys) 1149 | 1150 | let win = defaultview(doc); 1151 | #if JSC 1152 | double pagex = propd(win, "scrollX"); 1153 | double pagey = propd(win, "scrollY"); 1154 | #else 1155 | double pagex = webkit_dom_dom_window_get_scroll_x(win); 1156 | double pagey = webkit_dom_dom_window_get_scroll_y(win); 1157 | #endif 1158 | GSList *elms = makelist(page, doc, win, type, NULL, NULL); 1159 | g_object_unref(win); 1160 | 1161 | guint tnum = g_slist_length(elms); 1162 | 1163 | GString *hintstr = g_string_new(NULL); 1164 | 1165 | int keylen = strlen(hintkeys); 1166 | int iplen = ipkeys ? strlen(ipkeys) : 0; 1167 | int digit = getdigit(keylen, tnum); 1168 | bool last = iplen == digit; 1169 | elms = g_slist_reverse(elms); 1170 | int i = -1; 1171 | bool ret = false; 1172 | 1173 | bool rangein = false; 1174 | int rangeleft = getsetint(page, "hintrangemax"); 1175 | let rangeend = NULL; 1176 | 1177 | //tab key 1178 | bool focused = false; 1179 | bool dofocus = ipkeys && ipkeys[strlen(ipkeys) - 1] == 9; 1180 | if (dofocus) 1181 | ipkeys[strlen(ipkeys) - 1] = '\0'; 1182 | 1183 | char enterkey[2] = {0}; 1184 | *enterkey = (char)GDK_KEY_Return; 1185 | bool enter = page->rangestart && !g_strcmp0(enterkey, ipkeys); 1186 | if (type == Crange && (last || enter)) 1187 | for (GSList *next = elms; next; next = next->next) 1188 | { 1189 | Elm *elm = (Elm *)next->data; 1190 | let te = elm->elm; 1191 | i++; 1192 | char *key = sfree(makekey(hintkeys, keylen, tnum, i, digit)); 1193 | rangein |= !g_strcmp0(key, page->rangestart); 1194 | 1195 | if (page->rangestart && !rangein) 1196 | continue; 1197 | 1198 | if (enter) 1199 | { 1200 | rangeend = te; 1201 | if (--rangeleft < 0) break; 1202 | continue; 1203 | } 1204 | 1205 | if (!strcmp(key, ipkeys)) 1206 | { 1207 | ipkeys = NULL; 1208 | iplen = 0; 1209 | g_free(page->apkeys); 1210 | page->apkeys = NULL; 1211 | 1212 | if (!page->rangestart) 1213 | page->rangestart = g_strdup(key); 1214 | else 1215 | rangeend = te; 1216 | 1217 | break; 1218 | } 1219 | } 1220 | 1221 | GSList *rangeelms = NULL; 1222 | i = -1; 1223 | rangein = false; 1224 | rangeleft = getsetint(page, "hintrangemax"); 1225 | for (GSList *next = elms; next; next = next->next) 1226 | { 1227 | Elm *elm = (Elm *)next->data; 1228 | let te = elm->elm; 1229 | i++; 1230 | char *key = makekey(hintkeys, keylen, tnum, i, digit); 1231 | rangein |= !g_strcmp0(key, page->rangestart); 1232 | 1233 | if (dofocus) 1234 | { 1235 | if (!focused && 1236 | (type != Crange || !page->rangestart || rangein) && 1237 | g_str_has_prefix(key, ipkeys ?: "")) 1238 | { 1239 | focuselm(te); 1240 | focused = true; 1241 | } 1242 | } 1243 | else if (last && type != Crange) 1244 | { 1245 | if (!ret && !strcmp(key, ipkeys)) 1246 | { 1247 | ret = true; 1248 | focuselm(te); 1249 | if (type == Cclick) 1250 | { 1251 | bool isi = isinput(te); 1252 | if (page->script && !isi) 1253 | { 1254 | #if JSC 1255 | let rects = invoker(elm->elm, "getClientRects"); 1256 | let rect = idx(rects, 0); 1257 | #else 1258 | WebKitDOMClientRectList *rects = 1259 | webkit_dom_element_get_client_rects(elm->elm); 1260 | WebKitDOMClientRect *rect = 1261 | webkit_dom_client_rect_list_item(rects, 0); 1262 | #endif 1263 | double x, y, w, h; 1264 | recttovals(rect, &x, &y, &w, &h); 1265 | jscunref(rect); 1266 | g_object_unref(rects); 1267 | 1268 | char *arg = g_strdup_printf("%f:%f", 1269 | x + elm->fx + w / 2.0 + 1.0, 1270 | y + elm->fy + h / 2.0 + 1.0 1271 | ); 1272 | send(page, "click", arg); 1273 | g_free(arg); 1274 | } 1275 | else 1276 | { 1277 | if (!getsetbool(page, 1278 | "javascript-can-open-windows-automatically") 1279 | && sfree(attr(te, "TARGET"))) 1280 | send(page, "showmsg", "The element has target, may have to type the enter key."); 1281 | 1282 | #if JSC 1283 | let ce = invoker(doc, "createEvent", aS("MouseEvent")); 1284 | invoke(ce, "initEvent", aB(true), aB(true)); 1285 | invoke(te, "dispatchEvent", aJ(ce)); 1286 | #else 1287 | WebKitDOMEvent *ce = 1288 | webkit_dom_document_create_event(doc, "MouseEvent", NULL); 1289 | webkit_dom_event_init_event(ce, "click", true, true); 1290 | webkit_dom_event_target_dispatch_event( 1291 | (WebKitDOMEventTarget *)te, ce, NULL); 1292 | #endif 1293 | g_object_unref(ce); 1294 | } 1295 | 1296 | if (isi) 1297 | send(page, "toinsert", NULL); 1298 | else 1299 | send(page, "tonormal", NULL); 1300 | 1301 | } 1302 | else 1303 | { 1304 | hintret(page, type, te, false); 1305 | send(page, "tonormal", NULL); 1306 | } 1307 | } 1308 | } 1309 | else if (rangeend && rangein) 1310 | { 1311 | rangeelms = g_slist_prepend(rangeelms, g_object_ref(te)); 1312 | } 1313 | else if (!page->rangestart || (rangein && !rangeend)) 1314 | { 1315 | bool has = g_str_has_prefix(key, ipkeys ?: ""); 1316 | ret |= has; 1317 | if (has) 1318 | g_string_append(hintstr, sfree(makehintelm(page, 1319 | elm, key, iplen, pagex, pagey))); 1320 | } 1321 | 1322 | g_free(key); 1323 | clearelm(elm); 1324 | g_free(elm); 1325 | 1326 | if (rangein) 1327 | rangein = --rangeleft > 0 && rangeend != te; 1328 | } 1329 | 1330 | send(page, "_hintdata", hintstr->str); 1331 | g_string_free(hintstr, true); 1332 | 1333 | for (GSList *next = rangeelms; next; next = next->next) 1334 | { 1335 | hintret(page, type, next->data, next->next); 1336 | g_usleep(getsetint(page, "rangeloopusec")); 1337 | } 1338 | g_slist_free_full(rangeelms, g_object_unref); 1339 | 1340 | g_slist_free(elms); 1341 | 1342 | return ret; 1343 | } 1344 | 1345 | 1346 | //@context 1347 | 1348 | static void domfocusincb(let w, let e, Page *page) 1349 | { 1350 | let te = eachframes(page, activeelm); 1351 | send(page, "_focusuri", 1352 | sfree(tofull(te, te ? sfree(attr(te, "HREF")) : NULL))); 1353 | jscunref(te); 1354 | } 1355 | static void domfocusoutcb(let w, let e, Page *page) 1356 | { send(page, "_focusuri", NULL); } 1357 | //static void domactivatecb(WebKitDOMDOMWindow *w, WebKitDOMEvent *ev, Page *page) 1358 | //{ DD(domactivate!) } 1359 | static void rmtags(let doc, char *name) 1360 | { 1361 | let cl = getelms(doc, name); 1362 | 1363 | GSList *rms = NULL; 1364 | let te; 1365 | for (int i = 0; (te = idx(cl, i)); i++) 1366 | rms = g_slist_prepend(rms, te); 1367 | 1368 | for (GSList *next = rms; next; next = next->next) 1369 | { 1370 | #if JSC 1371 | let pn = prop(next->data, "parentNode"); 1372 | invoke(pn, "removeChiled", aJ(next->data)); 1373 | g_object_unref(pn); 1374 | g_object_unref(next->data); 1375 | #else 1376 | webkit_dom_node_remove_child( 1377 | webkit_dom_node_get_parent_node(next->data), next->data, NULL); 1378 | #endif 1379 | } 1380 | 1381 | g_slist_free(rms); 1382 | g_object_unref(cl); 1383 | } 1384 | static void domloadcb(let w, let e, let doc) 1385 | { 1386 | rmtags(doc, "NOSCRIPT"); 1387 | } 1388 | static gboolean _hintcb(Page *page) 1389 | { 1390 | if (page->hint) 1391 | makehint(page, page->lasttype, NULL, NULL); 1392 | if (page->hintcb) 1393 | g_source_remove(page->hintcb); 1394 | page->hintcb = 0; 1395 | return false; 1396 | } 1397 | static void hintcb(let w, let e, Page *page) 1398 | { _hintcb(page); } 1399 | static void dhintcb(let w, let e, Page *page) 1400 | { 1401 | if (!page->hintcb) 1402 | page->hintcb = g_timeout_add(400, (GSourceFunc)_hintcb, page); 1403 | } 1404 | static void unloadcb(let w, let e, Page *page) 1405 | { 1406 | GFA(page->apkeys, NULL) 1407 | } 1408 | static void pagestart(Page *page) 1409 | { 1410 | g_slist_free_full(page->black, g_free); 1411 | g_slist_free_full(page->white, g_free); 1412 | page->black = NULL; 1413 | page->white = NULL; 1414 | } 1415 | 1416 | static void addlistener(void *emt, char *name, void *func, void *data) 1417 | { 1418 | #if JSC 1419 | //this is disabled when javascript disabled 1420 | //also data is only sent once 1421 | // let f = jsc_value_new_function(jsc_value_get_context(emt), NULL, 1422 | // func, data, NULL, 1423 | // G_TYPE_NONE, 1, JSC_TYPE_VALUE, JSC_TYPE_VALUE); 1424 | // invoke(emt, "addEventListener", aS(name), aJ(f)); 1425 | // g_object_unref(f); 1426 | 1427 | // webkit_dom_event_target_add_event_listener( 1428 | // (void *)webkit_dom_node_for_js_value(emt), name, func, false, data); 1429 | #else 1430 | #endif 1431 | webkit_dom_event_target_add_event_listener(emt, name, func, false, data); 1432 | } 1433 | 1434 | static void *frameon(let doc, Page *page) 1435 | { 1436 | if (!doc) return NULL; 1437 | void *emt = 1438 | //have to be a view not a doc for beforeunload 1439 | #if JSC 1440 | //Somehow JSC's defaultView(conveted to the DOM) is not a event target 1441 | //Even it is, JSC's beforeunload may be killed 1442 | webkit_dom_document_get_default_view( 1443 | (void *)webkit_dom_node_for_js_value(g_object_ref(doc))); 1444 | #else 1445 | defaultview(doc); 1446 | #endif 1447 | 1448 | page->emitters = g_slist_prepend(page->emitters, emt); 1449 | 1450 | if (getsetbool(page, "rmnoscripttag")) 1451 | { 1452 | rmtags(doc, "NOSCRIPT"); 1453 | //have to monitor DOMNodeInserted or? 1454 | addlistener(emt, "DOMContentLoaded", domloadcb, doc); 1455 | } 1456 | 1457 | addlistener(emt, "DOMFocusIn" , domfocusincb , page); 1458 | addlistener(emt, "DOMFocusOut" , domfocusoutcb, page); 1459 | //addlistener(emt, "DOMActivate" , domactivatecb, page); 1460 | 1461 | //for hint 1462 | addlistener(emt, "resize" , hintcb , page); 1463 | addlistener(emt, "scroll" , hintcb , page); 1464 | addlistener(emt, "beforeunload", unloadcb, page); 1465 | addlistener(emt, "load" , dhintcb, page); 1466 | addlistener(emt, "DOMContentLoaded" , dhintcb, page); 1467 | addlistener(emt, "DOMFrameContentLoaded", dhintcb, page); 1468 | addlistener(emt, "DOMSubtreeModified" , dhintcb, page); 1469 | 1470 | return NULL; 1471 | } 1472 | 1473 | 1474 | static void pageon(Page *page, bool finished) 1475 | { 1476 | g_slist_free_full(page->emitters, g_object_unref); 1477 | page->emitters = NULL; 1478 | 1479 | eachframes(page, frameon); 1480 | 1481 | if (!finished 1482 | || !g_str_has_prefix(webkit_web_page_get_uri(page->kit), APP":main") 1483 | || !g_key_file_get_boolean(conf, "boot", "enablefavicon", NULL) 1484 | ) 1485 | return; 1486 | 1487 | let doc = sdoc(page); 1488 | 1489 | let cl = getelms(doc, "IMG"); 1490 | let te; 1491 | for (int j = 0; (te = idx(cl, j)); j++) 1492 | { 1493 | if (!g_strcmp0(sfree(attr(te, "SRC")), APP":F")) 1494 | { 1495 | #if JSC 1496 | let pe = prop(te, "parentElement"); 1497 | #else 1498 | WebKitDOMElement *pe = 1499 | webkit_dom_node_get_parent_element((WebKitDOMNode *)te); 1500 | #endif 1501 | char *f = g_strdup_printf( 1502 | APP":f/%s", sfree( 1503 | g_uri_escape_string( 1504 | sfree(attr(pe, "HREF")) ?: "", NULL, true))); 1505 | #if JSC 1506 | invoke(te, "setAttribute", aS("SRC"), aS(f)); 1507 | #else 1508 | webkit_dom_element_set_attribute(te, "SRC", f, NULL); 1509 | #endif 1510 | g_free(f); 1511 | jscunref(pe); 1512 | } 1513 | jscunref(te); 1514 | } 1515 | 1516 | g_object_unref(cl); 1517 | } 1518 | 1519 | 1520 | //@misc com funcs 1521 | static void mode(Page *page) 1522 | { 1523 | let te = eachframes(page, activeelm); 1524 | 1525 | if (te && (isinput(te) || attrb(te, "contenteditable"))) 1526 | send(page, "toinsert", NULL); 1527 | else 1528 | send(page, "tonormal", NULL); 1529 | 1530 | jscunref(te); 1531 | } 1532 | 1533 | static void *focusselection(let doc) 1534 | { 1535 | void *ret = NULL; 1536 | let win = defaultview(doc); 1537 | 1538 | #if JSC 1539 | let selection = invoker(win, "getSelection"); 1540 | 1541 | let an = 1542 | prop(selection, "anchorNode") 1543 | ?: prop(selection, "focusNode" ) 1544 | ?: prop(selection, "baseNode" ) 1545 | ?: prop(selection, "extentNode"); 1546 | 1547 | if (an) do 1548 | { 1549 | let pe = prop(an, "parentElement"); 1550 | if (pe && isins(clicktags, stag(pe))) 1551 | { 1552 | focuselm(pe); 1553 | g_object_unref(pe); 1554 | ret = pe; 1555 | pe = NULL; 1556 | } 1557 | g_object_unref(an); 1558 | an = pe; 1559 | } while (an); 1560 | 1561 | #else 1562 | WebKitDOMDOMSelection *selection = 1563 | webkit_dom_dom_window_get_selection(win); 1564 | 1565 | WebKitDOMNode *an = NULL; 1566 | 1567 | an = webkit_dom_dom_selection_get_anchor_node(selection) 1568 | ?: webkit_dom_dom_selection_get_focus_node(selection) 1569 | ?: webkit_dom_dom_selection_get_base_node(selection) 1570 | ?: webkit_dom_dom_selection_get_extent_node(selection); 1571 | 1572 | if (an) do 1573 | { 1574 | WebKitDOMElement *pe = webkit_dom_node_get_parent_element(an); 1575 | if (pe && isins(clicktags , stag(pe))) 1576 | { 1577 | focuselm(pe); 1578 | ret = pe; 1579 | break; 1580 | } 1581 | 1582 | } while ((an = webkit_dom_node_get_parent_node(an))); 1583 | 1584 | #endif 1585 | 1586 | g_object_unref(selection); 1587 | g_object_unref(win); 1588 | return ret; 1589 | } 1590 | 1591 | 1592 | static void blur(let doc) 1593 | { 1594 | let te = activeelm(doc); 1595 | if (te) 1596 | { 1597 | #if JSC 1598 | invoke(te, "blur"); 1599 | g_object_unref(te); 1600 | #else 1601 | webkit_dom_element_blur(te); 1602 | #endif 1603 | } 1604 | 1605 | //clear selection 1606 | 1607 | let win = defaultview(doc); 1608 | #if JSC 1609 | let selection = invoker(win, "getSelection"); 1610 | invoke(selection, "empty"); 1611 | #else 1612 | WebKitDOMDOMSelection *selection = 1613 | webkit_dom_dom_window_get_selection(win); 1614 | webkit_dom_dom_selection_empty(selection); 1615 | #endif 1616 | g_object_unref(win); 1617 | g_object_unref(selection); 1618 | } 1619 | 1620 | static void halfscroll(Page *page, bool d) 1621 | { 1622 | let win = defaultview(sdoc(page)); 1623 | 1624 | #if JSC 1625 | double h = propd(win, "innerHeight"); 1626 | invoke(win, "scrollTo", 1627 | aD(propd(win, "scrollX")), 1628 | aD(propd(win, "scrollY") + (d ? h/2 : - h/2))); 1629 | #else 1630 | double h = webkit_dom_dom_window_get_inner_height(win); 1631 | double y = webkit_dom_dom_window_get_scroll_y(win); 1632 | double x = webkit_dom_dom_window_get_scroll_x(win); 1633 | webkit_dom_dom_window_scroll_to(win, x, y + (d ? h/2 : - h/2)); 1634 | #endif 1635 | 1636 | g_object_unref(win); 1637 | } 1638 | 1639 | //@ipccb 1640 | static gboolean msgcb(WebKitWebPage *kp, WebKitUserMessage *msg, Page *page) 1641 | { 1642 | char **args = g_strsplit(webkit_user_message_get_name(msg), ":", 3); 1643 | 1644 | Coms type = *args[1]; 1645 | char *arg = args[2]; 1646 | 1647 | char *ipkeys = NULL; 1648 | switch (type) { 1649 | case Cload: 1650 | loadconf(); 1651 | break; 1652 | case Coverset: 1653 | GFA(page->overset, g_strdup(*arg ? arg : NULL)) 1654 | resetconf(page, webkit_web_page_get_uri(page->kit), true); 1655 | if (page->hint) 1656 | makehint(page, page->lasttype, NULL, g_strdup(page->apkeys)); 1657 | break; 1658 | case Cstart: 1659 | pagestart(page); 1660 | break; 1661 | case Con: 1662 | g_strfreev(page->refreq); 1663 | page->refreq = NULL; 1664 | 1665 | pageon(page, *arg == 'f'); 1666 | break; 1667 | 1668 | case Ckey: 1669 | { 1670 | if (page->hintcb) 1671 | { 1672 | g_source_remove(page->hintcb); 1673 | page->hintcb = g_timeout_add(400, (GSourceFunc)_hintcb, page); 1674 | } 1675 | 1676 | char key[2] = {0}; 1677 | key[0] = toupper(arg[0]); 1678 | ipkeys = page->apkeys ? 1679 | g_strconcat(page->apkeys, key, NULL) : g_strdup(key); 1680 | 1681 | type = page->lasttype; 1682 | arg = NULL; 1683 | } 1684 | case Cclick: 1685 | case Clink: 1686 | case Curi: 1687 | case Cspawn: 1688 | case Crange: 1689 | if (arg) 1690 | { 1691 | g_free(page->rangestart); 1692 | page->rangestart = NULL; 1693 | page->script = *arg == 'y'; 1694 | } 1695 | //gint64 start = g_get_monotonic_time(); 1696 | if (!(page->hint = makehint(page, type, getset(page, "hintkeys"), ipkeys))) 1697 | { 1698 | send(page, "showmsg", "No hint"); 1699 | send(page, "tonormal", NULL); 1700 | } 1701 | //D(time %f, (g_get_monotonic_time() - start) / 1000000.0) 1702 | break; 1703 | case Ctext: 1704 | { 1705 | GFA(ctexttext, *arg ? g_strdup(arg) : NULL); 1706 | let doc = sdoc(page); 1707 | let win = defaultview(doc); 1708 | makelist(page, doc, win, Ctext, NULL, NULL); 1709 | g_object_unref(win); 1710 | break; 1711 | } 1712 | case Crm: 1713 | page->hint = false; 1714 | GFA(page->apkeys, NULL) 1715 | break; 1716 | 1717 | case Cmode: 1718 | mode(page); 1719 | break; 1720 | 1721 | case Cfocus: 1722 | eachframes(page, focusselection); 1723 | break; 1724 | 1725 | case Cblur: 1726 | eachframes(page, blur); 1727 | break; 1728 | 1729 | case Cwhite: 1730 | if (*arg == 'r') setwblist(true); 1731 | if (*arg == 'n') setwblist(false); 1732 | if (*arg == 'w') showwhite(page, true); 1733 | if (*arg == 'b') showwhite(page, false); 1734 | break; 1735 | 1736 | case Ctlget: 1737 | textlinkget(page, arg); 1738 | break; 1739 | case Ctlset: 1740 | textlinkset(page, arg); 1741 | break; 1742 | 1743 | case Cwithref: 1744 | g_strfreev(page->refreq); 1745 | page->refreq = g_strsplit(arg, " ", 2); 1746 | break; 1747 | 1748 | case Cscroll: 1749 | halfscroll(page, *arg == 'd'); 1750 | break; 1751 | } 1752 | 1753 | g_strfreev(args); 1754 | return true; 1755 | } 1756 | 1757 | 1758 | //@page cbs 1759 | static void headerout(const char *name, const char *value, gpointer p) 1760 | { 1761 | g_print("%s : %s\n", name, value); 1762 | } 1763 | static gboolean reqcb( 1764 | WebKitWebPage *p, 1765 | WebKitURIRequest *req, 1766 | WebKitURIResponse *res, 1767 | Page *page) 1768 | { 1769 | page->pagereq++; 1770 | const char *reqstr = webkit_uri_request_get_uri(req); 1771 | D(reqcb %s, reqstr) 1772 | if (g_str_has_prefix(reqstr, APP":")) 1773 | return false; 1774 | 1775 | const char *pagestr = webkit_web_page_get_uri(page->kit); 1776 | SoupMessageHeaders *head = webkit_uri_request_get_http_headers(req); 1777 | 1778 | 1779 | bool ret = false; 1780 | int check = checkwb(reqstr); 1781 | if (check == 0) 1782 | ret = true; 1783 | else if (check == -1 && getsetbool(page, "adblock")) 1784 | { 1785 | bool (*checkf)(const char *, const char *) = 1786 | g_object_get_data(G_OBJECT(page->kit), APP"check"); 1787 | if (checkf) 1788 | ret = !checkf(reqstr, pagestr); 1789 | } 1790 | if (ret) goto out; 1791 | 1792 | if (res && page->pagereq == 2) 1793 | {//redirect. pagereq == 2 means it is a top level request 1794 | //in redirection we don't get yet current uri 1795 | resetconf(page, reqstr, false); 1796 | page->pagereq = 1; 1797 | page->redirected = true; 1798 | goto out; 1799 | } 1800 | 1801 | if (check == 1 //white 1802 | || page->pagereq == 1 //open page request 1803 | || !head 1804 | || !getsetbool(page, "reldomaindataonly") 1805 | || !soup_message_headers_get_list(head, "Referer") 1806 | ) goto out; 1807 | 1808 | //reldomainonly 1809 | GUri *puri = g_uri_parse(pagestr, SOUP_HTTP_URI_FLAGS, NULL); 1810 | const char *phost = g_uri_get_host(puri); 1811 | if (phost) 1812 | { 1813 | char **cuts = g_strsplit( 1814 | getset(page, "reldomaincutheads") ?: "", ";", -1); 1815 | for (char **cut = cuts; *cut; cut++) 1816 | if (g_str_has_prefix(phost, *cut)) 1817 | { 1818 | phost += strlen(*cut); 1819 | break; 1820 | } 1821 | g_strfreev(cuts); 1822 | 1823 | GUri *ruri = g_uri_parse(reqstr, SOUP_HTTP_URI_FLAGS, NULL); 1824 | const char *rhost = g_uri_get_host(ruri); 1825 | 1826 | ret = rhost && !g_str_has_suffix(rhost, phost); 1827 | 1828 | g_uri_unref(ruri); 1829 | } 1830 | g_uri_unref(puri); 1831 | 1832 | out: 1833 | if (ret) 1834 | addwhite(page, reqstr); 1835 | else 1836 | addblack(page, reqstr); 1837 | 1838 | if (!ret && head) 1839 | { 1840 | if (page->pagereq == 1 && (page->setagent || page->setagentprev)) 1841 | soup_message_headers_replace(head, "User-Agent", 1842 | getset(page, "user-agent") ?: ""); 1843 | 1844 | char *rmhdrs = getset(page, "removeheaders"); 1845 | if (rmhdrs) 1846 | { 1847 | char **rms = g_strsplit(rmhdrs, ";", -1); 1848 | for (char **rm = rms; *rm; rm++) 1849 | soup_message_headers_remove(head, *rm); 1850 | g_strfreev(rms); 1851 | } 1852 | } 1853 | 1854 | if (page->refreq && !g_strcmp0(page->refreq[1], reqstr)) 1855 | { 1856 | if (!ret && head) 1857 | soup_message_headers_append(head, "Referer", page->refreq[0]); 1858 | g_strfreev(page->refreq); 1859 | page->refreq = NULL; 1860 | } 1861 | 1862 | if (!ret && getsetbool(page, "stdoutheaders")) 1863 | { 1864 | if (res) 1865 | { 1866 | g_print("RESPONSE: %s\n", webkit_uri_response_get_uri(res)); 1867 | soup_message_headers_foreach( 1868 | webkit_uri_response_get_http_headers(res), headerout, NULL); 1869 | g_print("\n"); 1870 | } 1871 | g_print("REQUEST: %s\n", reqstr); 1872 | if (head) 1873 | soup_message_headers_foreach(head, headerout, NULL); 1874 | g_print("\n"); 1875 | } 1876 | 1877 | return ret; 1878 | } 1879 | //static void formcb(WebKitWebPage *page, GPtrArray *elms, gpointer p) {} 1880 | //static void loadcb(WebKitWebPage *kp, Page *page) {} 1881 | static void uricb(Page* page) 1882 | { 1883 | //workaround: when in redirect change uri delays 1884 | if (page->redirected) 1885 | page->pagereq = 1; 1886 | else 1887 | { 1888 | page->pagereq = 0; 1889 | resetconf(page, webkit_web_page_get_uri(page->kit), false); 1890 | } 1891 | page->redirected = false; 1892 | } 1893 | 1894 | 1895 | //static gboolean printopt(const char *option, JSCOptionType type, 1896 | // const char *description, gpointer user_data) 1897 | //{ 1898 | // D(option %s --- %s, option, description); 1899 | // return false; 1900 | //} 1901 | static void initpage(WebKitWebExtension *ex, WebKitWebPage *kp) 1902 | { 1903 | // jsc_options_foreach(printopt, NULL); 1904 | Page *page = g_new0(Page, 1); 1905 | g_object_weak_ref(G_OBJECT(kp), (GWeakNotify)freepage, page); 1906 | page->kit = kp; 1907 | #if JSC 1908 | page->mf = webkit_web_page_get_main_frame(kp); 1909 | #endif 1910 | page->seto = g_object_new(G_TYPE_OBJECT, NULL); 1911 | g_ptr_array_add(pages, page); 1912 | 1913 | wbpath = path2conf("whiteblack.conf"); 1914 | setwblist(false); 1915 | 1916 | loadconf(); 1917 | 1918 | // SIG( page->kit, "context-menu" , contextcb, NULL); 1919 | SIG( page->kit, "send-request" , reqcb , page); 1920 | SIG( page->kit, "user-message-received" , msgcb , page); 1921 | // SIG( page->kit, "document-loaded" , loadcb , page); 1922 | SIGW(page->kit, "notify::uri" , uricb , page); 1923 | // SIG( page->kit, "form-controls-associated", formcb , NULL); 1924 | } 1925 | G_MODULE_EXPORT void webkit_web_extension_initialize_with_user_data( 1926 | WebKitWebExtension *ex, const GVariant *v) 1927 | { 1928 | const char *str = g_variant_get_string((GVariant *)v, NULL); 1929 | fullname = g_strdup(g_strrstr(str, ";") + 1); 1930 | pages = g_ptr_array_new(); 1931 | SIG(ex, "page-created", initpage, NULL); 1932 | } 1933 | 1934 | -------------------------------------------------------------------------------- /general.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017-2020 jun7@hush.com 3 | 4 | This file is part of wyeb. 5 | 6 | wyeb is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | wyeb is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with wyeb. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define OLDNAME "wyebrowser" 28 | #define DIRNAME "wyeb." 29 | #define APP "wyeb" 30 | 31 | #define DSET "set;" 32 | #define MIMEOPEN "mimeopen -n %s" 33 | #define HINTKEYS "fsedagwrvxqcz" 34 | //bt324" 35 | #define DSEARCH "https://www.google.com/search?q=%s" 36 | #define DSEARCHKEY "g" 37 | 38 | //for webkit2gtk4.0. 4.1 has this 39 | #ifndef SOUP_HTTP_URI_FLAGS 40 | #define SOUP_HTTP_URI_FLAGS (G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_ENCODED_PATH | G_URI_FLAGS_ENCODED_QUERY | G_URI_FLAGS_ENCODED_FRAGMENT | G_URI_FLAGS_SCHEME_NORMALIZE) 41 | #endif 42 | 43 | #if ! DEBUG + 0 44 | #undef DEBUG 45 | #define DEBUG 0 46 | #endif 47 | 48 | #if DEBUG 49 | # define D(f, ...) g_print("#"#f"\n", __VA_ARGS__); 50 | # define DNN(f, ...) g_print(#f, __VA_ARGS__); 51 | # define DD(a) g_print("#"#a"\n"); 52 | # define DENUM(v, e) if (v == e) D(%s:%3d is %s, #v, v, #e); 53 | #else 54 | # define D(...) ; 55 | # define DNN(...) ; 56 | # define DD(a) ; 57 | # define DENUM(v, e) ; 58 | #endif 59 | 60 | #define SIG(o, n, c, u) \ 61 | g_signal_connect(o, n, G_CALLBACK(c), u) 62 | #define SIGA(o, n, c, u) \ 63 | g_signal_connect_after(o, n, G_CALLBACK(c), u) 64 | #define SIGW(o, n, c, u) \ 65 | g_signal_connect_swapped(o, n, G_CALLBACK(c), u) 66 | #define GFA(p, v) {void *__b = p; p = v; g_free(__b);} 67 | 68 | static char *sfree(char *p) 69 | { 70 | static void *s; 71 | g_free(s); 72 | return s = p; 73 | } 74 | 75 | static char *fullname = ""; 76 | static GKeyFile *conf; 77 | static char *confpath; 78 | 79 | typedef struct _WP WP; 80 | 81 | typedef enum { 82 | Cload = 'L', 83 | Coverset= 'O', 84 | Cstart = 's', 85 | Con = 'o', 86 | 87 | //hint 88 | Ckey = 'k', 89 | Cclick = 'c', 90 | Clink = 'l', 91 | Curi = 'u', 92 | Ctext = 't', 93 | Cspawn = 'S', 94 | Crange = 'r', 95 | Crm = 'R', 96 | 97 | Cmode = 'm', 98 | Cfocus = 'f', 99 | Cblur = 'b', 100 | Cwhite = 'w', 101 | Ctlset = 'T', 102 | Ctlget = 'g', 103 | Cwithref= 'W', 104 | Cscroll = 'v', 105 | } Coms; 106 | 107 | 108 | //@conf 109 | typedef struct { 110 | char *group; 111 | char *key; 112 | char *val; 113 | char *desc; 114 | } Conf; 115 | static Conf dconf[] = { 116 | {"all" , "winwidth" , "1000"}, 117 | {"all" , "winheight" , "1000"}, 118 | {"all" , "zoom" , "1.000"}, 119 | {"all" , "ignoretlserr" , "false"}, 120 | {"all" , "itp" , "false"}, 121 | {"all" , "histviewsize" , "99"}, 122 | {"all" , "histimgs" , "99"}, 123 | {"all" , "histimgsize" , "222"}, 124 | {"all" , "keepproc" , "false"}, 125 | //compatibility 126 | {"all" , "pointerwarp" , "false"}, 127 | 128 | // {"all" , "configreload" , "true", 129 | // "reload last window when whiteblack.conf or reldomain are changed"}, 130 | 131 | {"boot" , "enablefavicon", "true"}, 132 | {"boot" , "extensionargs", "adblock:true;"}, 133 | {"boot" , "multiwebprocs", "true"}, 134 | {"boot" , "ephemeral" , "false"}, 135 | {"boot" , "unsetGTK_OVERLAY_SCROLLING", "true", "workaround"}, 136 | 137 | {"search", DSEARCHKEY , DSEARCH}, 138 | {"search", "f" , "https://www.google.com/search?q=%s&btnI=I"}, 139 | {"search", "u" , "https://www.urbandictionary.com/define.php?term=%s"}, 140 | 141 | {"template", "na" , "%s"}, 142 | {"template", "h" , "http://%s"}, 143 | 144 | {"set:v" , "enable-caret-browsing", "true"}, 145 | {"set:v" , "hackedhint4js" , "false"}, 146 | {"set:script", "enable-javascript" , "true"}, 147 | {"set:image" , "auto-load-images" , "true"}, 148 | {"set:image" , "linkformat" , "[![]("APP":F) %.40s ](%s)"}, 149 | {"set:image" , "linkdata" , "tu"}, 150 | 151 | //core 152 | {DSET , "editor" , MIMEOPEN, 153 | "\ncore\n\n" 154 | "editor=xterm -e mimeopen %s\n" 155 | "editor=gvim --servername "APP" --remote-silent \"%s\"" 156 | }, 157 | {DSET , "mdeditor" , ""}, 158 | {DSET , "diropener" , MIMEOPEN, "diropener=xterm -e mimeopen %s"}, 159 | {DSET , "generator" , "markdown -f -style %s"}, 160 | 161 | //misc 162 | {DSET , "usercss" , "user.css", "\nmisc\n\nusercss=user.css;user2.css"}, 163 | {DSET , "userscripts" , ""}, 164 | {DSET , "search" , DSEARCHKEY, "search="DSEARCH}, 165 | {DSET , "searchstrmax" , "99"}, 166 | {DSET , "addressbar" , "false"}, 167 | {DSET , "msgcolor" , "#c07"}, 168 | {DSET , "msgmsec" , "600"}, 169 | {DSET , "keepfavicondb" , "false"}, 170 | {DSET , "newwinhandle" , "normal", "notnew | notnewall | ignore | back | normal"}, 171 | {DSET , "scriptdialog" , "true"}, 172 | 173 | //loading 174 | {DSET , "adblock" , "true", 175 | "\nloading\n\nadblock has a point only while "APP"adblock is working." 176 | }, 177 | {DSET , "reldomaindataonly", "false"}, 178 | {DSET , "reldomaincutheads", "www.;wiki.;bbs.;developer."}, 179 | {DSET , "showblocked" , "false"}, 180 | {DSET , "stdoutheaders" , "false"}, 181 | {DSET , "removeheaders" , "", 182 | "removeheaders=Upgrade-Insecure-Requests;Referer;"}, 183 | {DSET , "rmnoscripttag" , "false"}, 184 | 185 | //bookmark 186 | {DSET , "linkformat" , "[%.40s ](%s)", "\nbookmark\n"}, 187 | {DSET , "linkdata" , "tu", "t: title, u: uri, f: favicon"}, 188 | 189 | //hint 190 | {DSET , "hintkeys" , HINTKEYS, "\nhint\n"}, 191 | {DSET , "hackedhint4js" , "true"}, 192 | {DSET , "hintrangemax" , "9"}, 193 | {DSET , "rangeloopusec" , "90000"}, 194 | 195 | //dl 196 | {DSET , "dlwithheaders" , "false", "\ndownload\n"}, 197 | {DSET , "dlmimetypes" , "", 198 | "dlmimetypes=text/plain;video/;audio/;application/\n" 199 | "dlmimetypes=*"}, 200 | {DSET , "dlsubdir" , ""}, 201 | {DSET , "dlwinback" , "true"}, 202 | {DSET , "dlwinclosemsec" , "3000"}, 203 | 204 | //script 205 | {DSET , "spawnmsg" , "false", "\nscript\n"}, 206 | 207 | {DSET , "onstartmenu" , "", 208 | "onstartmenu exec a file in the menu dir when load started before redirect"}, 209 | {DSET , "onloadmenu" , "", "when load commited"}, 210 | {DSET , "onloadedmenu" , "", "when load finished"}, 211 | 212 | //input 213 | {DSET , "multiplescroll" , "2", "\ninput\n"}, 214 | {DSET , "keybindswaps" , "", 215 | "keybindswaps=Xx;ZZ;zZ ->if typed x: x to X, if Z: Z to Z"}, 216 | {DSET , "hjkl2arrowkeys" , "false", 217 | "hjkl's defaults are scrolls, not arrow keys"}, 218 | {DSET , "mdlbtnlinkaction" , "openback"}, 219 | {DSET , "mdlbtnspace" , "winlist"}, 220 | {DSET , "mdlbtnleft" , "prevwin"}, 221 | {DSET , "mdlbtnright" , "nextwin"}, 222 | {DSET , "mdlbtnup" , "top"}, 223 | {DSET , "mdlbtndown" , "bottom"}, 224 | {DSET , "pressscrollup" , "top"}, 225 | {DSET , "pressscrolldown" , "bottom"}, 226 | {DSET , "rockerleft" , "back"}, 227 | {DSET , "rockerright" , "forward"}, 228 | {DSET , "rockerup" , "quitprev"}, 229 | {DSET , "rockerdown" , "quitnext"}, 230 | {DSET , "button8" , "back"}, 231 | {DSET , "button9" , "forward"}, 232 | 233 | //changes 234 | //{DSET , "auto-load-images" , "false"}, 235 | //{DSET , "enable-plugins" , "false"}, 236 | //{DSET , "enable-java" , "false"}, 237 | //{DSET , "enable-fullscreen", "false"}, 238 | }; 239 | #ifdef MAINC 240 | static bool confbool(char *key) 241 | { return g_key_file_get_boolean(conf, "all", key, NULL); } 242 | static int confint(char *key) 243 | { return g_key_file_get_integer(conf, "all", key, NULL); } 244 | static double confdouble(char *key) 245 | { return g_key_file_get_double(conf, "all", key, NULL); } 246 | #endif 247 | static char *confcstr(char *key) 248 | {//return is static string 249 | static char *str; 250 | GFA(str, g_key_file_get_string(conf, "all", key, NULL)) 251 | return str ? *str ? str : NULL : NULL; 252 | } 253 | static char *getset(WP *wp, char *key) 254 | {//return is static string 255 | if (!wp) 256 | { 257 | static char *ret; 258 | GFA(ret, g_key_file_get_string(conf, DSET, key, NULL)) 259 | return ret ? *ret ? ret : NULL : NULL; 260 | } 261 | return confcstr(key) ?: g_object_get_data(wp->seto, key); 262 | } 263 | static bool getsetbool(WP *wp, char *key) 264 | { return !g_strcmp0(getset(wp, key), "true"); } 265 | static int getsetint(WP *wp, char *key) 266 | { return atoi(getset(wp, key) ?: "0"); } 267 | #ifdef MAINC 268 | static char **getsetsplit(WP *wp, char *key) 269 | { 270 | char *tmp = getset(wp, key); 271 | return tmp ? g_strsplit(tmp, ";", -1) : NULL; 272 | } 273 | #endif 274 | 275 | 276 | static char *path2conf(const char *name) 277 | { 278 | return g_build_filename( 279 | g_get_user_config_dir(), fullname, name, NULL); 280 | } 281 | 282 | static bool setprop(WP *wp, GKeyFile *kf, char *group, char *key) 283 | { 284 | if (!g_key_file_has_key(kf, group, key, NULL)) return false; 285 | char *val = g_key_file_get_string(kf, group, key, NULL); 286 | g_object_set_data_full(wp->seto, key, *val ? val : NULL, g_free); 287 | return true; 288 | } 289 | static void setprops(WP *wp, GKeyFile *kf, char *group) 290 | { 291 | //sets 292 | static int deps; 293 | if (deps > 99) return; 294 | char **sets = g_key_file_get_string_list(kf, group, "sets", NULL, NULL); 295 | for (char **set = sets; set && *set; set++) { 296 | char *setstr = g_strdup_printf("set:%s", *set); 297 | deps++; 298 | setprops(wp, kf, setstr); 299 | deps--; 300 | g_free(setstr); 301 | } 302 | g_strfreev(sets); 303 | 304 | //D(set props group: %s, group) 305 | #ifdef MAINC 306 | _kitprops(true, wp->seto, kf, group); 307 | #else 308 | setprop(wp, kf, group, "javascript-can-open-windows-automatically"); 309 | if (setprop(wp, kf, group, "user-agent") && strcmp(group, DSET)) 310 | wp->setagent = true; 311 | #endif 312 | 313 | //non webkit settings 314 | int len = sizeof(dconf) / sizeof(*dconf); 315 | for (int i = 0; i < len; i++) 316 | if (!strcmp(dconf[i].group, DSET)) 317 | setprop(wp, kf, group, dconf[i].key); 318 | } 319 | 320 | static GSList *regs; 321 | static GSList *regsrev; 322 | static void makeuriregs() { 323 | for (GSList *next = regs; next; next = next->next) 324 | { 325 | regfree(((void **)next->data)[0]); 326 | g_free( ((void **)next->data)[1]); 327 | } 328 | g_slist_free_full(regs, g_free); 329 | regs = NULL; 330 | g_slist_free(regsrev); 331 | regsrev = NULL; 332 | 333 | char **groups = g_key_file_get_groups(conf, NULL); 334 | for (char **next = groups; *next; next++) 335 | { 336 | char *gl = *next; 337 | if (!g_str_has_prefix(gl, "uri:")) continue; 338 | 339 | char *g = gl; 340 | char *tofree = NULL; 341 | if (g_key_file_has_key(conf, g, "reg", NULL)) 342 | { 343 | g = tofree = g_key_file_get_value(conf, g, "reg", NULL); 344 | } else { 345 | g += 4; 346 | } 347 | 348 | void **reg = g_new(void*, 2); 349 | *reg = g_new(regex_t, 1); 350 | // if (regcomp(*reg, g, REG_EXTENDED | REG_NOSUB)) 351 | if (!g || regcomp(*reg, g, REG_EXTENDED)) 352 | { //failed 353 | g_free(*reg); 354 | g_free(reg); 355 | } else { 356 | reg[1] = g_strdup(gl); 357 | regsrev = g_slist_prepend(regsrev, reg); 358 | } 359 | g_free(tofree); 360 | } 361 | g_strfreev(groups); 362 | 363 | for (GSList *next = regsrev; next; next = next->next) 364 | regs = g_slist_prepend(regs, next->data); 365 | } 366 | static bool eachuriconf(WP *wp, const char* uri, bool lastone, 367 | bool (*func)(WP *, const char *uri, char *group)) 368 | { 369 | bool ret = false; 370 | regmatch_t match[2]; 371 | for (GSList *reg = lastone ? regsrev : regs; reg; reg = reg->next) 372 | if (regexec(*(void **)reg->data, uri, 2, match, 0) == 0) 373 | { 374 | char *m = match[1].rm_so == -1 ? NULL : g_strndup( 375 | uri + match[1].rm_so, match[1].rm_eo - match[1].rm_so); 376 | 377 | ret = func(wp, m ?: uri, ((void **)reg->data)[1]) || ret; 378 | g_free(m); 379 | if (lastone) break; 380 | } 381 | 382 | return ret; 383 | } 384 | static bool seturiprops(WP *wp, const char *uri, char *group) 385 | { 386 | setprops(wp, conf, group); 387 | return true; 388 | } 389 | 390 | static void _resetconf(WP *wp, const char *uri, bool force) 391 | { 392 | //clearing. don't worry about reg and handler they are not set 393 | if (force || (wp->lasturiconf && g_strcmp0(wp->lasturiconf, uri))) 394 | setprops(wp, conf, DSET); 395 | GFA(wp->lasturiconf, NULL) 396 | 397 | GFA(wp->lastreset, g_strdup(uri)) 398 | if (uri && eachuriconf(wp, uri, false, seturiprops)) 399 | GFA(wp->lasturiconf, g_strdup(uri)) 400 | if (wp->overset) { 401 | char **sets = g_strsplit(wp->overset, "/", -1); 402 | for (char **set = sets; *set; set++) 403 | { 404 | char *setstr = g_strdup_printf("set:%s", *set); 405 | setprops(wp, conf, setstr); 406 | g_free(setstr); 407 | } 408 | g_strfreev(sets); 409 | } 410 | } 411 | static void initconf(GKeyFile *kf) 412 | { 413 | if (conf) g_key_file_free(conf); 414 | conf = kf ?: g_key_file_new(); 415 | makeuriregs(); 416 | 417 | int len = sizeof(dconf) / sizeof(*dconf); 418 | for (int i = 0; i < len; i++) 419 | { 420 | Conf c = dconf[i]; 421 | 422 | if (g_key_file_has_key(conf, c.group, c.key, NULL)) continue; 423 | if (kf) 424 | { 425 | if (!strcmp(c.group, "search")) continue; 426 | if (!strcmp(c.group, "template")) continue; 427 | if (g_str_has_prefix(c.group, "set:")) continue; 428 | } 429 | 430 | g_key_file_set_value(conf, c.group, c.key, c.val); 431 | if (c.desc) 432 | g_key_file_set_comment(conf, c.group, c.key, c.desc, NULL); 433 | } 434 | 435 | #ifdef MAINC 436 | //fill vals not set 437 | if (LASTWIN) 438 | _kitprops(false, LASTWIN->seto, conf, DSET); 439 | else { 440 | WebKitSettings *set = webkit_settings_new(); 441 | _kitprops(false, (GObject *)set, conf, DSET); 442 | g_object_unref(set); 443 | } 444 | 445 | if (kf) return; 446 | 447 | //sample and comment 448 | g_key_file_set_comment(conf, "all", NULL, 449 | "Basically "APP" doesn't cut spaces." 450 | " Also true is only 'true' not 'True'" 451 | "\n\n'all' overwrites the 'set's", NULL); 452 | g_key_file_set_comment(conf, "template", NULL, 453 | "\nUnlike the search group, the arg is not escaped\n" 454 | "but can be called the same as the search", NULL); 455 | g_key_file_set_comment(conf, "set:v", NULL, 456 | "\nSettings set. You can add set:*\n" 457 | "It is enabled by actions(set/set2/setstack) or included by others" 458 | , NULL); 459 | g_key_file_set_comment(conf, DSET, NULL, 460 | "\n\nDefaults of 'set's\n" 461 | "You can use set;'s keys in set:* and uri:*\n", NULL); 462 | //enable-javascript is first value 463 | g_key_file_set_comment(conf, DSET, "enable-javascript", "\nWebkit's settings\n", NULL); 464 | g_key_file_set_comment(conf, DSET, "hardware-acceleration-policy", 465 | "ON_DEMAND | ALWAYS | NEVER", NULL); 466 | 467 | const char *sample = "uri:^https?://(www\\.)?foo\\.bar/.*"; 468 | 469 | g_key_file_set_boolean(conf, sample, "enable-javascript", true); 470 | g_key_file_set_comment(conf, sample, NULL, 471 | "\nAfter 'uri:' is regular expressions for the setting set.\n" 472 | "preferential order of groups: lower > upper > '"DSET"'" 473 | , NULL); 474 | 475 | g_key_file_set_string(conf, sample, "sets", "image;script"); 476 | g_key_file_set_comment(conf, sample, "sets", 477 | "Include sets. This works even in set:*" , NULL); 478 | 479 | sample = "uri:^foo|a-zA-Z0-9|*"; 480 | 481 | g_key_file_set_string(conf, sample, "reg", "^foo[^a-zA-Z0-9]*$"); 482 | g_key_file_set_comment(conf, sample, "reg", 483 | "Use reg if the regular expression has []" 484 | , NULL); 485 | g_key_file_set_string(conf, sample, "handler", "chromium %s"); 486 | g_key_file_set_comment(conf, sample, "handler", 487 | "handler cancels request before sent and\n" 488 | "spawns the command with a URI matched the reg\n" 489 | "This works only in uri:*" 490 | , NULL); 491 | g_key_file_set_string(conf, sample, "handlerunesc", "false"); 492 | g_key_file_set_string(conf, sample, "handlerescchrs", ""); 493 | #endif 494 | } 495 | 496 | 497 | //@misc 498 | #ifdef MAINC 499 | static bool _mkdirif(char *path, bool isfile) 500 | { 501 | bool ret = false; 502 | char *dir = isfile ? g_path_get_dirname(path) : path; 503 | 504 | if (!g_file_test(dir, G_FILE_TEST_EXISTS)) 505 | ret = !g_mkdir_with_parents(dir, 0700); 506 | 507 | g_assert(g_file_test(dir, G_FILE_TEST_IS_DIR)); 508 | 509 | if (isfile) g_free(dir); 510 | 511 | return ret; 512 | } 513 | static void mkdirif(char *path) 514 | { 515 | _mkdirif(path, true); 516 | } 517 | #endif 518 | 519 | static char *_escape(const char *str, char *esc) 520 | { 521 | gulong len = 0; 522 | for (const char *c = str; *c; c++) 523 | { 524 | len++; 525 | for (char *e = esc; *e; e++) 526 | if (*e == *c) 527 | { 528 | len++; 529 | break; 530 | } 531 | } 532 | char ret[len + 1]; 533 | ret[len] = '\0'; 534 | 535 | gulong i = 0; 536 | for (const char *c = str; *c; c++) 537 | { 538 | for (char *e = esc; *e; e++) 539 | if (*e == *c) 540 | { 541 | ret[i++] = '\\'; 542 | break; 543 | } 544 | 545 | ret[i++] = *c; 546 | } 547 | 548 | return g_strdup(ret); 549 | } 550 | static char *regesc(const char *str) 551 | { 552 | return _escape(str, ".?+"); 553 | } 554 | 555 | 556 | #ifdef MAINC 557 | //@ipc 558 | static char *ipcpath(char *name) 559 | { 560 | static char *path; 561 | GFA(path, g_build_filename(g_get_user_runtime_dir(), fullname, name, NULL)); 562 | mkdirif(path); 563 | return path; 564 | } 565 | 566 | static void ipccb(const char *line); 567 | static gboolean ipcgencb(GIOChannel *ch, GIOCondition c, gpointer p) 568 | { 569 | char *line; 570 | // GError *err = NULL; 571 | g_io_channel_read_line(ch, &line, NULL, NULL, NULL); 572 | // if (err) 573 | // { 574 | // D("ioerr: ", err->message); 575 | // g_error_free(err); 576 | // } 577 | if (!line) return true; 578 | g_strchomp(line); 579 | 580 | //D(receive %s, line) 581 | char *unesc = g_strcompress(line); 582 | ipccb(unesc); 583 | g_free(unesc); 584 | g_free(line); 585 | return true; 586 | } 587 | 588 | static bool ipcsend(char *name, char *str) { 589 | char *path = ipcpath(name); 590 | bool ret = false; 591 | int cpipe = 0; 592 | if ( 593 | (g_file_test(path, G_FILE_TEST_EXISTS)) && 594 | (cpipe = open(path, O_WRONLY | O_NONBLOCK))) 595 | { 596 | D(ipcsend start %s %s, name, str) 597 | char *esc = g_strescape(str, ""); 598 | char *send = g_strconcat(esc, "\n", NULL); 599 | int len = strlen(send); 600 | if (len > PIPE_BUF) 601 | fcntl(cpipe, F_SETFL, 0); 602 | ret = write(cpipe, send, len) == len; 603 | g_free(send); 604 | g_free(esc); 605 | close(cpipe); 606 | 607 | D(ipcsend -end- %s %s %b, name, str, ret) 608 | } 609 | return ret; 610 | } 611 | static void ipcwatch(char *name, GMainContext *ctx) { 612 | char *path = ipcpath(name); 613 | 614 | if (!g_file_test(path, G_FILE_TEST_EXISTS)) 615 | mkfifo(path, 0600); 616 | 617 | GIOChannel *io = g_io_channel_new_file(path, "r+", NULL); 618 | GSource *watch = g_io_create_watch(io, G_IO_IN); 619 | g_io_channel_unref(io); 620 | g_source_set_callback(watch, (GSourceFunc)ipcgencb, NULL, NULL); 621 | g_source_attach(watch, ctx); 622 | g_source_unref(watch); 623 | } 624 | #endif 625 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | WEBKITVER ?= 4.0 3 | WEBKIT ?= webkit2gtk-$(WEBKITVER) 4 | EXTENSION_DIR ?= $(PREFIX)/lib/wyebrowser 5 | DISTROURI ?= https://archlinux.org/ 6 | DISTRONAME ?= "Arch Linux" 7 | 8 | ifneq ($(WEBKITVER), 4.0) 9 | VERDIR=/$(WEBKITVER) 10 | endif 11 | ifeq ($(DEBUG), 1) 12 | CFLAGS += -Wall -Wno-deprecated-declarations 13 | else 14 | CFLAGS += -Wno-deprecated-declarations 15 | endif 16 | 17 | all: wyeb ext.so 18 | 19 | wyeb: main.c general.c makefile 20 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< \ 21 | `pkg-config --cflags --libs gtk+-3.0 glib-2.0 $(WEBKIT)` \ 22 | -DEXTENSION_DIR=\"$(EXTENSION_DIR)$(VERDIR)\" \ 23 | -DDISTROURI=\"$(DISTROURI)\" \ 24 | -DDISTRONAME=\"$(DISTRONAME)\" \ 25 | -DDEBUG=${DEBUG} -lm 26 | 27 | ext.so: ext.c general.c makefile 28 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -shared -fPIC \ 29 | `pkg-config --cflags --libs gtk+-3.0 glib-2.0 $(WEBKIT)` \ 30 | -DDEBUG=${DEBUG} -DJSC=${JSC} 31 | 32 | clean: 33 | rm -f wyeb ext.so 34 | 35 | install: all 36 | install -Dm755 wyeb $(DESTDIR)$(PREFIX)/bin/wyeb 37 | install -Dm755 ext.so $(DESTDIR)$(EXTENSION_DIR)$(VERDIR)/ext.so 38 | install -Dm644 wyeb.png $(DESTDIR)$(PREFIX)/share/pixmaps/wyeb.png 39 | install -Dm644 wyeb.desktop $(DESTDIR)$(PREFIX)/share/applications/wyeb.desktop 40 | 41 | uninstall: 42 | rm -f $(PREFIX)/bin/wyeb 43 | rm -f $(EXTENSION_DIR)$(VERDIR)/ext.so 44 | -rmdir $(EXTENSION_DIR)$(VERDIR) 45 | -rmdir $(EXTENSION_DIR) 46 | rm -f $(PREFIX)/share/pixmaps/wyeb.png 47 | rm -f $(PREFIX)/share/applications/wyeb.desktop 48 | 49 | 50 | re: clean all 51 | # $(MAKE) clean 52 | # $(MAKE) all 53 | 54 | full: re install 55 | -------------------------------------------------------------------------------- /testrun.sh: -------------------------------------------------------------------------------- 1 | 2 | cd `dirname $0` 3 | export PATH="$PATH:`pwd`" 4 | 5 | export DEBUG=1 6 | make clean 7 | make > /dev/null 2>&1 8 | 9 | if test -e wyeb; then 10 | ./wyeb 11 | else 12 | export DEBUG=0 13 | #show error 14 | make 15 | fi 16 | 17 | make clean 18 | -------------------------------------------------------------------------------- /wyeb.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=wyeb. 3 | Exec=wyeb %u 4 | StartupWMClass=wyeb. 5 | Icon=wyeb 6 | Terminal=false 7 | Type=Application 8 | Categories=Network;WebBrowser; 9 | MimeType=text/html; 10 | -------------------------------------------------------------------------------- /wyeb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jun7/wyeb/62594d4a5889f876baec00af2ec05c83f51ba8b9/wyeb.png --------------------------------------------------------------------------------